/* Overlay manager for SPU. Copyright (C) 2006-2021 Free Software Foundation, Inc. This file is part of the GNU Binutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ /* MFC DMA defn's. */ #define MFC_GET_CMD 0x40 #define MFC_MAX_DMA_SIZE 0x4000 #define MFC_TAG_UPDATE_ALL 2 #define MFC_TAG_ID 0 /* Register usage. */ #define reserved1 $75 #define parm $75 #define tab1 reserved1 #define tab2 reserved1 #define vma reserved1 #define oldvma reserved1 #define newmask reserved1 #define map reserved1 #define reserved2 $76 #define off1 reserved2 #define off2 reserved2 #define present1 reserved2 #define present2 reserved2 #define sz reserved2 #define cmp reserved2 #define add64 reserved2 #define cgbits reserved2 #define off3 reserved2 #define off4 reserved2 #define addr4 reserved2 #define off5 reserved2 #define tagstat reserved2 #define reserved3 $77 #define size1 reserved3 #define size2 reserved3 #define rv3 reserved3 #define ealo reserved3 #define cmd reserved3 #define off64 reserved3 #define tab3 reserved3 #define tab4 reserved3 #define tab5 reserved3 #define reserved4 $78 #define ovl reserved4 #define rv2 reserved4 #define rv5 reserved4 #define cgshuf reserved4 #define newovl reserved4 #define irqtmp1 reserved4 #define irqtmp2 reserved4 #define reserved5 $79 #define target reserved5 #define save1 $74 #define rv4 save1 #define rv7 save1 #define tagid save1 #define maxsize save1 #define pbyte save1 #define pbit save1 #define save2 $73 #define cur save2 #define rv6 save2 #define osize save2 #define zovl save2 #define oldovl save2 #define newvma save2 #define save3 $72 #define rv1 save3 #define ea64 save3 #define buf3 save3 #define genwi save3 #define newmap save3 #define oldmask save3 #define save4 $71 #define irq_stat save4 .text .align 4 .type __rv_pattern, @object .size __rv_pattern, 16 __rv_pattern: .word 0x00010203, 0x10111213, 0x80808080, 0x80808080 .type __cg_pattern, @object .size __cg_pattern, 16 __cg_pattern: .word 0x04050607, 0x80808080, 0x80808080, 0x80808080 .type __ovly_current, @object .size __ovly_current, 16 __ovly_current: .space 16 /* * __ovly_return - stub for returning from overlay functions. * * On entry the four slots of $lr are: * __ovly_return, prev ovl index, caller return addr, undefined. * * Load the previous overlay and jump to the caller return address. * Updates __ovly_current. */ .align 4 .global __ovly_return .type __ovly_return, @function __ovly_return: ila tab1, _ovly_table - 16 # 0,2 0 shlqbyi ovl, $lr, 4 # 1,4 0 #nop shlqbyi target, $lr, 8 # 1,4 1 #nop; lnop #nop; lnop shli off1, ovl, 4 # 0,4 4 #lnop #nop hbr ovly_ret9, target # 1,15 5 #nop; lnop #nop; lnop #nop lqx vma, tab1, off1 # 1,6 8 #ifdef OVLY_IRQ_SAVE nop stqd save4, -64($sp) # 1,6 9 #else #nop; lnop #endif #nop; lnop #nop; lnop #nop; lnop #nop; lnop #nop rotqbyi size1, vma, 4 # 1,4 14 #nop stqd save3, -48($sp) # 1,6 15 #nop stqd save2, -32($sp) # 1,6 16 #nop stqd save1, -16($sp) # 1,6 17 andi present1, size1, 1 # 0,2 18 stqr ovl, __ovly_current # 1,6 18 #nop; lnop #nop brz present1, do_load # 1,4 20 ovly_ret9: #nop bi target # 1,4 21 /* * __ovly_load - copy an overlay partion to local store. * * On entry $75 points to a word consisting of the overlay index in * the top 14 bits, and the target address in the bottom 18 bits. * * Sets up $lr to return via __ovly_return. If $lr is already set * to return via __ovly_return, don't change it. In that case we * have a tail call from one overlay function to another. * Updates __ovly_current. */ .align 3 .global __ovly_load .type __ovly_load, @function __ovly_load: #if OVL_STUB_SIZE == 8 ######## #nop lqd target, 0(parm) # 1,6 -11 #nop; lnop #nop; lnop #nop; lnop #nop; lnop #nop; lnop #nop rotqby target, target, parm # 1,4 -5 ila tab2, _ovly_table - 16 # 0,2 -4 stqd save3, -48($sp) # 1,6 -4 #nop stqd save2, -32($sp) # 1,6 -3 #nop stqd save1, -16($sp) # 1,6 -2 rotmi ovl, target, -18 # 0,4 -1 hbr ovly_load9, target # 1,15 -1 ila rv1, __ovly_return # 0,2 0 #lnop #nop; lnop #nop lqr cur, __ovly_current # 1,6 2 shli off2, ovl, 4 # 0,4 3 stqr ovl, __ovly_current # 1,6 3 ceq rv2, $lr, rv1 # 0,2 4 lqr rv3, __rv_pattern # 1,6 4 #nop; lnop #nop; lnop #nop lqx vma, tab2, off2 # 1,6 7 ######## #else /* OVL_STUB_SIZE == 16 */ ######## ila tab2, _ovly_table - 16 # 0,2 0 stqd save3, -48($sp) # 1,6 0 ila rv1, __ovly_return # 0,2 1 stqd save2, -32($sp) # 1,6 1 shli off2, ovl, 4 # 0,4 2 lqr cur, __ovly_current # 1,6 2 nop stqr ovl, __ovly_current # 1,6 3 ceq rv2, $lr, rv1 # 0,2 4 lqr rv3, __rv_pattern # 1,6 4 #nop hbr ovly_load9, target # 1,15 5 #nop lqx vma, tab2, off2 # 1,6 6 #nop stqd save1, -16($sp) # 1,6 7 ######## #endif #nop; lnop #nop; lnop #nop shufb rv4, rv1, cur, rv3 # 1,4 10 #nop fsmb rv5, rv2 # 1,4 11 #nop rotqmbyi rv6, $lr, -8 # 1,4 12 #nop rotqbyi size2, vma, 4 # 1,4 13 #nop lqd save3, -48($sp) # 1,6 14 #nop; lnop or rv7, rv4, rv6 # 0,2 16 lqd save2, -32($sp) # 1,6 16 andi present2, size2, 1 # 0,2 17 #ifdef OVLY_IRQ_SAVE stqd save4, -64($sp) # 1,6 17 #else lnop # 1,0 17 #endif selb $lr, rv7, $lr, rv5 # 0,2 18 lqd save1, -16($sp) # 1,6 18 #nop brz present2, do_load # 1,4 19 ovly_load9: #nop bi target # 1,4 20 /* If we get here, we are about to load a new overlay. * "vma" contains the relevant entry from _ovly_table[]. * extern struct { * u32 vma; * u32 size; * u32 file_offset; * u32 buf; * } _ovly_table[]; */ .align 3 .global __ovly_load_event .type __ovly_load_event, @function __ovly_load_event: do_load: #ifdef OVLY_IRQ_SAVE ila irqtmp1, do_load10 # 0,2 -5 rotqbyi sz, vma, 8 # 1,4 -5 #nop rdch irq_stat, $SPU_RdMachStat # 1,6 -4 #nop bid irqtmp1 # 1,4 -3 do_load10: nop #else #nop rotqbyi sz, vma, 8 # 1,4 0 #endif rotqbyi osize, vma, 4 # 1,4 1 #nop lqa ea64, _EAR_ # 1,6 2 #nop lqr cgshuf, __cg_pattern # 1,6 3 /* We could predict the branch at the end of this loop by adding a few instructions, and there are plenty of free cycles to do so without impacting loop execution time. However, it doesn't make a great deal of sense since we need to wait for the dma to complete anyway. */ __ovly_xfer_loop: #nop rotqmbyi off64, sz, -4 # 1,4 4 #nop; lnop #nop; lnop #nop; lnop cg cgbits, ea64, off64 # 0,2 8 #lnop #nop; lnop #nop shufb add64, cgbits, cgbits, cgshuf # 1,4 10 #nop; lnop #nop; lnop #nop; lnop addx add64, ea64, off64 # 0,2 14 #lnop ila maxsize, MFC_MAX_DMA_SIZE # 0,2 15 lnop ori ea64, add64, 0 # 0,2 16 rotqbyi ealo, add64, 4 # 1,4 16 cgt cmp, osize, maxsize # 0,2 17 wrch $MFC_LSA, vma # 1,6 17 #nop; lnop selb sz, osize, maxsize, cmp # 0,2 19 wrch $MFC_EAH, ea64 # 1,6 19 ila tagid, MFC_TAG_ID # 0,2 20 wrch $MFC_EAL, ealo # 1,6 20 ila cmd, MFC_GET_CMD # 0,2 21 wrch $MFC_Size, sz # 1,6 21 sf osize, sz, osize # 0,2 22 wrch $MFC_TagId, tagid # 1,6 22 a vma, vma, sz # 0,2 23 wrch $MFC_Cmd, cmd # 1,6 23 #nop brnz osize, __ovly_xfer_loop # 1,4 24 /* Now update our data structions while waiting for DMA to complete. Low bit of .size needs to be cleared on the _ovly_table entry corresponding to the evicted overlay, and set on the entry for the newly loaded overlay. Note that no overlay may in fact be evicted as _ovly_buf_table[] starts with all zeros. Don't zap .size entry for zero index! Also of course update the _ovly_buf_table entry. */ #nop lqr newovl, __ovly_current # 1,6 25 #nop; lnop #nop; lnop #nop; lnop #nop; lnop #nop; lnop shli off3, newovl, 4 # 0,4 31 #lnop ila tab3, _ovly_table - 16 # 0,2 32 #lnop #nop fsmbi pbyte, 0x100 # 1,4 33 #nop; lnop #nop lqx vma, tab3, off3 # 1,6 35 #nop; lnop andi pbit, pbyte, 1 # 0,2 37 lnop #nop; lnop #nop; lnop #nop; lnop or newvma, vma, pbit # 0,2 41 rotqbyi buf3, vma, 12 # 1,4 41 #nop; lnop #nop stqx newvma, tab3, off3 # 1,6 43 #nop; lnop shli off4, buf3, 2 # 1,4 45 #lnop ila tab4, _ovly_buf_table - 4 # 0,2 46 #lnop #nop; lnop #nop; lnop #nop lqx map, tab4, off4 # 1,6 49 #nop cwx genwi, tab4, off4 # 1,4 50 a addr4, tab4, off4 # 0,2 51 #lnop #nop; lnop #nop; lnop #nop; lnop #nop rotqby oldovl, map, addr4 # 1,4 55 #nop shufb newmap, newovl, map, genwi # 0,4 56 #if MFC_TAG_ID < 16 ila newmask, 1 << MFC_TAG_ID # 0,2 57 #else ilhu newmask, 1 << (MFC_TAG_ID - 16) # 0,2 57 #endif #lnop #nop; lnop #nop; lnop stqd newmap, 0(addr4) # 1,6 60 /* Save app's tagmask, wait for DMA complete, restore mask. */ ila tagstat, MFC_TAG_UPDATE_ALL # 0,2 61 rdch oldmask, $MFC_RdTagMask # 1,6 61 #nop wrch $MFC_WrTagMask, newmask # 1,6 62 #nop wrch $MFC_WrTagUpdate, tagstat # 1,6 63 #nop rdch tagstat, $MFC_RdTagStat # 1,6 64 #nop sync # 1,4 65 /* Any hint prior to the sync is lost. A hint here allows the branch to complete 15 cycles after the hint. With no hint the branch will take 18 or 19 cycles. */ ila tab5, _ovly_table - 16 # 0,2 66 hbr do_load99, target # 1,15 66 shli off5, oldovl, 4 # 0,4 67 wrch $MFC_WrTagMask, oldmask # 1,6 67 ceqi zovl, oldovl, 0 # 0,2 68 #lnop #nop; lnop #nop fsm zovl, zovl # 1,4 70 #nop lqx oldvma, tab5, off5 # 1,6 71 #nop lqd save3, -48($sp) # 1,6 72 #nop; lnop andc pbit, pbit, zovl # 0,2 74 lqd save2, -32($sp) # 1,6 74 #ifdef OVLY_IRQ_SAVE ila irqtmp2, do_load90 # 0,2 75 #lnop andi irq_stat, irq_stat, 1 # 0,2 76 #lnop #else #nop; lnop #nop; lnop #endif andc oldvma, oldvma, pbit # 0,2 77 lqd save1, -16($sp) # 1,6 77 nop # 0,0 78 #lnop #nop stqx oldvma, tab5, off5 # 1,6 79 #nop #ifdef OVLY_IRQ_SAVE binze irq_stat, irqtmp2 # 1,4 80 do_load90: #nop lqd save4, -64($sp) # 1,6 84 #else #nop; lnop #endif .global _ovly_debug_event .type _ovly_debug_event, @function _ovly_debug_event: nop /* Branch to target address. */ do_load99: bi target # 1,4 81/85 .size __ovly_load, . - __ovly_load