2012-11-15 00:39:56 +01:00
# include "stdafx.h"
2018-05-14 22:07:36 +02:00
# include "Utilities/JIT.h"
2017-02-22 11:10:55 +01:00
# include "Utilities/lockless.h"
2017-07-18 19:03:47 +02:00
# include "Utilities/sysinfo.h"
2014-08-23 02:16:54 +02:00
# include "Emu/Memory/Memory.h"
2014-06-02 19:27:24 +02:00
# include "Emu/System.h"
2014-08-26 01:55:37 +02:00
# include "Emu/IdManager.h"
# include "Emu/Cell/PPUThread.h"
2016-04-14 01:09:41 +02:00
# include "Emu/Cell/ErrorCodes.h"
# include "Emu/Cell/lv2/sys_spu.h"
# include "Emu/Cell/lv2/sys_event_flag.h"
# include "Emu/Cell/lv2/sys_event.h"
# include "Emu/Cell/lv2/sys_interrupt.h"
2014-08-22 23:15:02 +02:00
2014-08-26 01:55:37 +02:00
# include "Emu/Cell/SPUDisAsm.h"
2012-11-15 00:39:56 +01:00
# include "Emu/Cell/SPUThread.h"
# include "Emu/Cell/SPUInterpreter.h"
2014-04-06 21:23:32 +02:00
# include "Emu/Cell/SPURecompiler.h"
2017-02-17 20:35:57 +01:00
# include "Emu/Cell/RawSPUThread.h"
2016-04-25 12:49:12 +02:00
2016-05-13 15:55:34 +02:00
# include <cmath>
2014-07-16 14:09:20 +02:00
# include <cfenv>
2017-04-23 22:09:27 +02:00
# include <atomic>
# include <thread>
2018-05-14 22:07:36 +02:00
# include <shared_mutex>
2017-07-18 19:03:47 +02:00
2017-12-09 15:57:43 +01:00
const bool s_use_ssse3 =
# ifdef _MSC_VER
utils : : has_ssse3 ( ) ;
# elif __SSSE3__
true ;
# else
false ;
2018-02-27 17:03:00 +01:00
# define _mm_shuffle_epi8
2017-12-09 15:57:43 +01:00
# endif
2017-02-17 20:35:57 +01:00
# ifdef _MSC_VER
bool operator = = ( const u128 & lhs , const u128 & rhs )
{
return lhs . lo = = rhs . lo & & lhs . hi = = rhs . hi ;
}
# endif
2014-07-16 14:09:20 +02:00
2015-07-06 01:21:15 +02:00
extern u64 get_timebased_time ( ) ;
2017-07-24 19:55:11 +02:00
extern u64 get_system_time ( ) ;
2015-07-06 01:21:15 +02:00
2018-05-27 22:37:01 +02:00
extern const spu_decoder < spu_interpreter_precise > g_spu_interpreter_precise ;
2017-12-09 15:57:43 +01:00
2018-05-27 22:37:01 +02:00
extern const spu_decoder < spu_interpreter_fast > g_spu_interpreter_fast ;
2015-08-26 04:54:06 +02:00
2018-05-27 22:37:01 +02:00
extern thread_local u64 g_tls_fault_spu ;
2017-04-23 22:09:27 +02:00
2017-05-20 13:45:02 +02:00
template < >
void fmt_class_string < spu_decoder_type > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( spu_decoder_type type )
{
switch ( type )
{
case spu_decoder_type : : precise : return " Interpreter (precise) " ;
case spu_decoder_type : : fast : return " Interpreter (fast) " ;
case spu_decoder_type : : asmjit : return " Recompiler (ASMJIT) " ;
case spu_decoder_type : : llvm : return " Recompiler (LLVM) " ;
}
return unknown ;
} ) ;
}
2018-04-30 18:39:06 +02:00
template < >
void fmt_class_string < spu_block_size_type > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( spu_block_size_type type )
{
switch ( type )
{
case spu_block_size_type : : safe : return " Safe " ;
case spu_block_size_type : : mega : return " Mega " ;
case spu_block_size_type : : giga : return " Giga " ;
}
return unknown ;
} ) ;
}
2017-07-11 21:21:22 +02:00
namespace spu
{
namespace scheduler
{
2017-07-18 00:12:13 +02:00
std : : array < std : : atomic < u8 > , 65536 > atomic_instruction_table = { } ;
2017-07-24 19:55:11 +02:00
constexpr u32 native_jiffy_duration_us = 1500 ; //About 1ms resolution with a half offset
2017-07-11 21:21:22 +02:00
void acquire_pc_address ( u32 pc , u32 timeout_ms = 3 )
{
2017-07-14 16:00:49 +02:00
const u8 max_concurrent_instructions = ( u8 ) g_cfg . core . preferred_spu_threads ;
2017-07-18 00:12:13 +02:00
const u32 pc_offset = pc > > 2 ;
2017-07-11 21:21:22 +02:00
2017-07-24 19:55:11 +02:00
if ( atomic_instruction_table [ pc_offset ] . load ( std : : memory_order_consume ) > = max_concurrent_instructions )
2017-07-14 16:00:49 +02:00
{
2017-07-24 19:55:11 +02:00
if ( timeout_ms > 0 )
2017-07-14 16:00:49 +02:00
{
2017-10-21 13:21:37 +02:00
const u64 timeout = timeout_ms * 1000u ; //convert to microseconds
const u64 start = get_system_time ( ) ;
2017-07-24 19:55:11 +02:00
auto remaining = timeout ;
while ( atomic_instruction_table [ pc_offset ] . load ( std : : memory_order_consume ) > = max_concurrent_instructions )
{
if ( remaining > = native_jiffy_duration_us )
std : : this_thread : : sleep_for ( 1 ms ) ;
else
2018-05-28 15:52:21 +02:00
std : : this_thread : : yield ( ) ;
2017-07-24 19:55:11 +02:00
const auto now = get_system_time ( ) ;
const auto elapsed = now - start ;
if ( elapsed > timeout ) break ;
remaining = timeout - elapsed ;
}
}
else
{
//Slight pause if function is overburdened
2017-10-21 13:21:37 +02:00
const auto count = atomic_instruction_table [ pc_offset ] . load ( std : : memory_order_consume ) * 100ull ;
busy_wait ( count ) ;
2017-07-14 16:00:49 +02:00
}
2017-07-11 21:21:22 +02:00
}
2017-07-18 00:12:13 +02:00
atomic_instruction_table [ pc_offset ] + + ;
2017-07-11 21:21:22 +02:00
}
void release_pc_address ( u32 pc )
{
2017-07-18 00:12:13 +02:00
const u32 pc_offset = pc > > 2 ;
2017-07-11 21:21:22 +02:00
2017-07-18 00:12:13 +02:00
atomic_instruction_table [ pc_offset ] - - ;
2017-07-11 21:21:22 +02:00
}
struct concurrent_execution_watchdog
{
u32 pc = 0 ;
2017-07-14 16:00:49 +02:00
bool active = false ;
2017-07-11 21:21:22 +02:00
concurrent_execution_watchdog ( SPUThread & spu )
: pc ( spu . pc )
{
2017-07-14 16:00:49 +02:00
if ( g_cfg . core . preferred_spu_threads > 0 )
{
acquire_pc_address ( pc , ( u32 ) g_cfg . core . spu_delay_penalty ) ;
active = true ;
}
2017-07-11 21:21:22 +02:00
}
~ concurrent_execution_watchdog ( )
{
2017-07-14 16:00:49 +02:00
if ( active )
release_pc_address ( pc ) ;
2017-07-11 21:21:22 +02:00
}
} ;
}
}
2018-05-18 17:51:48 +02:00
const auto spu_putllc_tx = build_function_asm < bool ( * ) ( u32 raddr , u64 rtime , const void * _old , const void * _new ) > ( [ ] ( asmjit : : X86Assembler & c , auto & args )
2018-05-14 22:07:36 +02:00
{
using namespace asmjit ;
Label fall = c . newLabel ( ) ;
Label fail = c . newLabel ( ) ;
// Prepare registers
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_reservations ) ) ;
c . mov ( x86 : : r10 , x86 : : qword_ptr ( x86 : : rax ) ) ;
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_base_addr ) ) ;
c . mov ( x86 : : r11 , x86 : : qword_ptr ( x86 : : rax ) ) ;
c . lea ( x86 : : r11 , x86 : : qword_ptr ( x86 : : r11 , args [ 0 ] ) ) ;
c . shr ( args [ 0 ] , 4 ) ;
c . lea ( x86 : : r10 , x86 : : qword_ptr ( x86 : : r10 , args [ 0 ] ) ) ;
2018-05-18 17:51:48 +02:00
c . mov ( args [ 0 ] . r32 ( ) , 3 ) ;
2018-05-14 22:07:36 +02:00
2018-05-17 17:56:54 +02:00
// Prepare data (Windows has only 6 volatile vector registers)
2018-05-14 22:07:36 +02:00
c . vmovups ( x86 : : ymm0 , x86 : : yword_ptr ( args [ 2 ] , 0 ) ) ;
c . vmovups ( x86 : : ymm1 , x86 : : yword_ptr ( args [ 2 ] , 32 ) ) ;
c . vmovups ( x86 : : ymm2 , x86 : : yword_ptr ( args [ 2 ] , 64 ) ) ;
c . vmovups ( x86 : : ymm3 , x86 : : yword_ptr ( args [ 2 ] , 96 ) ) ;
2018-05-17 17:56:54 +02:00
# ifdef _WIN32
c . vmovups ( x86 : : ymm4 , x86 : : yword_ptr ( args [ 3 ] , 0 ) ) ;
c . vmovups ( x86 : : ymm5 , x86 : : yword_ptr ( args [ 3 ] , 96 ) ) ;
# else
2018-05-14 22:07:36 +02:00
c . vmovups ( x86 : : ymm6 , x86 : : yword_ptr ( args [ 3 ] , 0 ) ) ;
c . vmovups ( x86 : : ymm7 , x86 : : yword_ptr ( args [ 3 ] , 32 ) ) ;
c . vmovups ( x86 : : ymm8 , x86 : : yword_ptr ( args [ 3 ] , 64 ) ) ;
c . vmovups ( x86 : : ymm9 , x86 : : yword_ptr ( args [ 3 ] , 96 ) ) ;
# endif
// Begin transaction
2018-05-18 17:51:48 +02:00
Label begin = build_transaction_enter ( c , fall ) ;
2018-05-14 22:07:36 +02:00
c . cmp ( x86 : : qword_ptr ( x86 : : r10 ) , args [ 1 ] ) ;
c . jne ( fail ) ;
c . vxorps ( x86 : : ymm0 , x86 : : ymm0 , x86 : : yword_ptr ( x86 : : r11 , 0 ) ) ;
c . vxorps ( x86 : : ymm1 , x86 : : ymm1 , x86 : : yword_ptr ( x86 : : r11 , 32 ) ) ;
c . vxorps ( x86 : : ymm2 , x86 : : ymm2 , x86 : : yword_ptr ( x86 : : r11 , 64 ) ) ;
c . vxorps ( x86 : : ymm3 , x86 : : ymm3 , x86 : : yword_ptr ( x86 : : r11 , 96 ) ) ;
c . vorps ( x86 : : ymm0 , x86 : : ymm0 , x86 : : ymm1 ) ;
c . vorps ( x86 : : ymm1 , x86 : : ymm2 , x86 : : ymm3 ) ;
c . vorps ( x86 : : ymm0 , x86 : : ymm1 , x86 : : ymm0 ) ;
c . vptest ( x86 : : ymm0 , x86 : : ymm0 ) ;
c . jnz ( fail ) ;
# ifdef _WIN32
2018-05-17 17:56:54 +02:00
c . vmovups ( x86 : : ymm2 , x86 : : yword_ptr ( args [ 3 ] , 32 ) ) ;
c . vmovups ( x86 : : ymm3 , x86 : : yword_ptr ( args [ 3 ] , 64 ) ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 0 ) , x86 : : ymm4 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 32 ) , x86 : : ymm2 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 64 ) , x86 : : ymm3 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 96 ) , x86 : : ymm5 ) ;
2018-05-14 22:07:36 +02:00
# else
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 0 ) , x86 : : ymm6 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 32 ) , x86 : : ymm7 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 64 ) , x86 : : ymm8 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 96 ) , x86 : : ymm9 ) ;
# endif
2018-05-20 15:38:33 +02:00
c . add ( x86 : : qword_ptr ( x86 : : r10 ) , 1 ) ;
2018-05-14 22:07:36 +02:00
c . xend ( ) ;
c . vzeroupper ( ) ;
c . mov ( x86 : : eax , 1 ) ;
c . ret ( ) ;
2018-05-17 21:19:26 +02:00
// Touch memory after transaction failure
2018-05-14 22:07:36 +02:00
c . bind ( fall ) ;
2018-05-18 17:51:48 +02:00
c . sub ( args [ 0 ] . r32 ( ) , 1 ) ;
c . jz ( fail ) ;
c . sar ( x86 : : eax , 24 ) ;
c . js ( fail ) ;
2018-05-17 21:19:26 +02:00
c . lock ( ) . add ( x86 : : qword_ptr ( x86 : : r11 ) , 0 ) ;
c . lock ( ) . add ( x86 : : qword_ptr ( x86 : : r10 ) , 0 ) ;
2018-05-18 17:51:48 +02:00
# ifdef _WIN32
c . vmovups ( x86 : : ymm4 , x86 : : yword_ptr ( args [ 3 ] , 0 ) ) ;
c . vmovups ( x86 : : ymm5 , x86 : : yword_ptr ( args [ 3 ] , 96 ) ) ;
# endif
c . jmp ( begin ) ;
2018-05-14 22:07:36 +02:00
c . bind ( fail ) ;
build_transaction_abort ( c , 0xff ) ;
2018-05-18 17:51:48 +02:00
c . xor_ ( x86 : : eax , x86 : : eax ) ;
c . ret ( ) ;
2018-05-14 22:07:36 +02:00
} ) ;
2018-05-19 22:20:41 +02:00
const auto spu_getll_tx = build_function_asm < u64 ( * ) ( u32 raddr , void * rdata , u64 * out_rtime ) > ( [ ] ( asmjit : : X86Assembler & c , auto & args )
2018-05-14 22:07:36 +02:00
{
using namespace asmjit ;
Label fall = c . newLabel ( ) ;
// Prepare registers
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_reservations ) ) ;
c . mov ( x86 : : r10 , x86 : : qword_ptr ( x86 : : rax ) ) ;
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_base_addr ) ) ;
c . mov ( x86 : : r11 , x86 : : qword_ptr ( x86 : : rax ) ) ;
c . lea ( x86 : : r11 , x86 : : qword_ptr ( x86 : : r11 , args [ 0 ] ) ) ;
c . shr ( args [ 0 ] , 4 ) ;
c . lea ( x86 : : r10 , x86 : : qword_ptr ( x86 : : r10 , args [ 0 ] ) ) ;
2018-05-19 22:20:41 +02:00
c . mov ( args [ 0 ] . r32 ( ) , 1 ) ;
2018-05-14 22:07:36 +02:00
// Begin transaction
2018-05-18 17:51:48 +02:00
Label begin = build_transaction_enter ( c , fall ) ;
2018-05-14 22:07:36 +02:00
c . mov ( x86 : : rax , x86 : : qword_ptr ( x86 : : r10 ) ) ;
c . vmovaps ( x86 : : ymm0 , x86 : : yword_ptr ( x86 : : r11 , 0 ) ) ;
c . vmovaps ( x86 : : ymm1 , x86 : : yword_ptr ( x86 : : r11 , 32 ) ) ;
c . vmovaps ( x86 : : ymm2 , x86 : : yword_ptr ( x86 : : r11 , 64 ) ) ;
c . vmovaps ( x86 : : ymm3 , x86 : : yword_ptr ( x86 : : r11 , 96 ) ) ;
c . xend ( ) ;
c . vmovups ( x86 : : yword_ptr ( args [ 1 ] , 0 ) , x86 : : ymm0 ) ;
c . vmovups ( x86 : : yword_ptr ( args [ 1 ] , 32 ) , x86 : : ymm1 ) ;
c . vmovups ( x86 : : yword_ptr ( args [ 1 ] , 64 ) , x86 : : ymm2 ) ;
c . vmovups ( x86 : : yword_ptr ( args [ 1 ] , 96 ) , x86 : : ymm3 ) ;
c . vzeroupper ( ) ;
2018-05-19 22:20:41 +02:00
c . mov ( x86 : : qword_ptr ( args [ 2 ] ) , x86 : : rax ) ;
c . mov ( x86 : : rax , args [ 0 ] ) ;
2018-05-14 22:07:36 +02:00
c . ret ( ) ;
2018-05-17 21:19:26 +02:00
// Touch memory after transaction failure
2018-05-14 22:07:36 +02:00
c . bind ( fall ) ;
2018-05-18 17:51:48 +02:00
c . pause ( ) ;
2018-05-17 21:19:26 +02:00
c . mov ( x86 : : rax , x86 : : qword_ptr ( x86 : : r11 ) ) ;
c . mov ( x86 : : rax , x86 : : qword_ptr ( x86 : : r10 ) ) ;
2018-05-19 22:20:41 +02:00
c . add ( args [ 0 ] , 1 ) ;
2018-05-18 17:51:48 +02:00
c . jmp ( begin ) ;
2018-05-14 22:07:36 +02:00
} ) ;
2018-05-19 22:14:02 +02:00
const auto spu_putlluc_tx = build_function_asm < u64 ( * ) ( u32 raddr , const void * rdata ) > ( [ ] ( asmjit : : X86Assembler & c , auto & args )
2018-05-14 22:07:36 +02:00
{
using namespace asmjit ;
Label fall = c . newLabel ( ) ;
// Prepare registers
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_reservations ) ) ;
c . mov ( x86 : : r10 , x86 : : qword_ptr ( x86 : : rax ) ) ;
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_base_addr ) ) ;
c . mov ( x86 : : r11 , x86 : : qword_ptr ( x86 : : rax ) ) ;
c . lea ( x86 : : r11 , x86 : : qword_ptr ( x86 : : r11 , args [ 0 ] ) ) ;
c . shr ( args [ 0 ] , 4 ) ;
c . lea ( x86 : : r10 , x86 : : qword_ptr ( x86 : : r10 , args [ 0 ] ) ) ;
2018-05-19 22:14:02 +02:00
c . mov ( args [ 0 ] . r32 ( ) , 1 ) ;
2018-05-14 22:07:36 +02:00
// Prepare data
c . vmovups ( x86 : : ymm0 , x86 : : yword_ptr ( args [ 1 ] , 0 ) ) ;
c . vmovups ( x86 : : ymm1 , x86 : : yword_ptr ( args [ 1 ] , 32 ) ) ;
c . vmovups ( x86 : : ymm2 , x86 : : yword_ptr ( args [ 1 ] , 64 ) ) ;
c . vmovups ( x86 : : ymm3 , x86 : : yword_ptr ( args [ 1 ] , 96 ) ) ;
// Begin transaction
2018-05-18 17:51:48 +02:00
Label begin = build_transaction_enter ( c , fall ) ;
2018-05-14 22:07:36 +02:00
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 0 ) , x86 : : ymm0 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 32 ) , x86 : : ymm1 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 64 ) , x86 : : ymm2 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : r11 , 96 ) , x86 : : ymm3 ) ;
2018-05-20 15:38:33 +02:00
c . add ( x86 : : qword_ptr ( x86 : : r10 ) , 1 ) ;
2018-05-14 22:07:36 +02:00
c . xend ( ) ;
c . vzeroupper ( ) ;
2018-05-19 22:14:02 +02:00
c . mov ( x86 : : rax , args [ 0 ] ) ;
2018-05-14 22:07:36 +02:00
c . ret ( ) ;
2018-05-17 21:19:26 +02:00
// Touch memory after transaction failure
2018-05-14 22:07:36 +02:00
c . bind ( fall ) ;
2018-05-18 17:51:48 +02:00
c . pause ( ) ;
2018-05-17 21:19:26 +02:00
c . lock ( ) . add ( x86 : : qword_ptr ( x86 : : r11 ) , 0 ) ;
c . lock ( ) . add ( x86 : : qword_ptr ( x86 : : r10 ) , 0 ) ;
2018-05-19 22:14:02 +02:00
c . add ( args [ 0 ] , 1 ) ;
2018-05-18 17:51:48 +02:00
c . jmp ( begin ) ;
2018-05-14 22:07:36 +02:00
} ) ;
2015-07-12 23:02:02 +02:00
void spu_int_ctrl_t : : set ( u64 ints )
{
// leave only enabled interrupts
2015-09-18 00:41:14 +02:00
ints & = mask ;
2015-07-12 23:02:02 +02:00
// notify if at least 1 bit was set
2016-04-14 01:09:41 +02:00
if ( ints & & ~ stat . fetch_or ( ints ) & ints & & tag )
2015-07-12 23:02:02 +02:00
{
2017-02-05 00:26:57 +01:00
reader_lock rlock ( id_manager : : g_mutex ) ;
2015-07-12 23:02:02 +02:00
2017-02-04 17:30:21 +01:00
if ( tag )
2015-07-12 23:02:02 +02:00
{
2017-02-04 17:30:21 +01:00
if ( auto handler = tag - > handler . lock ( ) )
{
handler - > exec ( ) ;
}
2015-07-12 23:02:02 +02:00
}
}
}
2015-08-26 04:54:06 +02:00
const spu_imm_table_t g_spu_imm ;
2015-03-20 17:53:54 +01:00
2016-05-13 15:55:34 +02:00
spu_imm_table_t : : scale_table_t : : scale_table_t ( )
{
for ( s32 i = - 155 ; i < 174 ; i + + )
{
2018-05-28 12:40:31 +02:00
m_data [ i + 155 ] . vf = _mm_set1_ps ( static_cast < float > ( std : : exp2 ( i ) ) ) ;
2016-05-13 15:55:34 +02:00
}
}
spu_imm_table_t : : spu_imm_table_t ( )
{
for ( u32 i = 0 ; i < sizeof ( sldq_pshufb ) / sizeof ( sldq_pshufb [ 0 ] ) ; i + + )
{
for ( u32 j = 0 ; j < 16 ; j + + )
{
sldq_pshufb [ i ] . _u8 [ j ] = static_cast < u8 > ( j - i ) ;
}
}
for ( u32 i = 0 ; i < sizeof ( srdq_pshufb ) / sizeof ( srdq_pshufb [ 0 ] ) ; i + + )
{
2017-12-09 15:57:43 +01:00
const u32 im = ( 0u - i ) & 0x1f ;
2016-05-13 15:55:34 +02:00
for ( u32 j = 0 ; j < 16 ; j + + )
{
2017-12-09 15:57:43 +01:00
srdq_pshufb [ i ] . _u8 [ j ] = ( j + im > 15 ) ? 0xff : static_cast < u8 > ( j + im ) ;
2016-05-13 15:55:34 +02:00
}
}
for ( u32 i = 0 ; i < sizeof ( rldq_pshufb ) / sizeof ( rldq_pshufb [ 0 ] ) ; i + + )
{
for ( u32 j = 0 ; j < 16 ; j + + )
{
rldq_pshufb [ i ] . _u8 [ j ] = static_cast < u8 > ( ( j - i ) & 0xf ) ;
}
}
}
2017-04-23 22:09:27 +02:00
void SPUThread : : on_spawn ( )
{
2017-10-21 13:21:37 +02:00
if ( g_cfg . core . thread_scheduler_enabled )
2017-04-23 22:09:27 +02:00
{
2017-10-21 13:21:37 +02:00
thread_ctrl : : set_thread_affinity_mask ( thread_ctrl : : get_affinity_mask ( thread_class : : spu ) ) ;
2017-04-23 22:09:27 +02:00
}
2017-05-20 13:45:02 +02:00
if ( g_cfg . core . lower_spu_priority )
2017-04-23 22:09:27 +02:00
{
2017-06-24 17:36:49 +02:00
thread_ctrl : : set_native_priority ( - 1 ) ;
2017-04-23 22:09:27 +02:00
}
}
2017-03-11 00:14:48 +01:00
void SPUThread : : on_init ( const std : : shared_ptr < void > & _this )
{
if ( ! offset )
{
const_cast < u32 & > ( offset ) = verify ( " SPU LS " HERE , vm : : alloc ( 0x40000 , vm : : main ) ) ;
cpu_thread : : on_init ( _this ) ;
}
}
2015-11-26 09:06:29 +01:00
std : : string SPUThread : : get_name ( ) const
{
2016-07-27 23:43:22 +02:00
return fmt : : format ( " %sSPU[0x%x] Thread (%s) " , offset > = RAW_SPU_BASE_ADDR ? " Raw " : " " , id , m_name ) ;
2012-11-15 00:39:56 +01:00
}
2016-04-14 01:09:41 +02:00
std : : string SPUThread : : dump ( ) const
2014-07-16 14:09:20 +02:00
{
2018-03-24 22:03:32 +01:00
std : : string ret = cpu_thread : : dump ( ) ;
2015-07-01 00:25:52 +02:00
2018-04-03 21:42:47 +02:00
// Print some transaction statistics
2018-04-09 16:45:37 +02:00
fmt : : append ( ret , " \n Blocks: %u; Fail: %u " , block_counter , block_failure ) ;
2018-05-18 17:53:01 +02:00
fmt : : append ( ret , " \n [%s] " , ch_mfc_cmd ) ;
2018-03-24 22:03:32 +01:00
fmt : : append ( ret , " \n Tag Mask: 0x%08x " , ch_tag_mask ) ;
fmt : : append ( ret , " \n MFC Stall: 0x%08x " , ch_stall_mask ) ;
fmt : : append ( ret , " \n MFC Queue Size: %u " , mfc_size ) ;
for ( u32 i = 0 ; i < 16 ; i + + )
{
if ( i < mfc_size )
{
2018-05-18 17:53:01 +02:00
fmt : : append ( ret , " \n %s " , mfc_queue [ i ] ) ;
2018-03-24 22:03:32 +01:00
}
else
{
fmt : : append ( ret , " \n [-] " ) ;
}
}
ret + = " \n Registers: \n ========= " ;
for ( u32 i = 0 ; i < 128 ; i + + )
{
fmt : : append ( ret , " \n GPR[%d] = %s " , i , gpr [ i ] ) ;
}
2014-07-16 14:09:20 +02:00
2016-04-14 01:09:41 +02:00
return ret ;
2012-11-15 00:39:56 +01:00
}
2016-04-14 01:09:41 +02:00
void SPUThread : : cpu_init ( )
2012-11-15 00:39:56 +01:00
{
2015-08-26 04:54:06 +02:00
gpr = { } ;
fpscr . Reset ( ) ;
2015-03-02 03:10:41 +01:00
2017-02-17 20:35:57 +01:00
ch_mfc_cmd = { } ;
2013-11-03 20:23:16 +01:00
2017-02-13 14:12:24 +01:00
srr0 = 0 ;
2018-03-24 22:03:32 +01:00
mfc_size = 0 ;
2018-04-03 21:42:47 +02:00
mfc_barrier = 0 ;
mfc_fence = 0 ;
2017-02-17 20:35:57 +01:00
ch_tag_upd = 0 ;
2015-03-13 02:59:25 +01:00
ch_tag_mask = 0 ;
2017-08-31 21:03:47 +02:00
mfc_prxy_mask = 0 ;
2015-09-18 00:41:14 +02:00
ch_tag_stat . data . store ( { } ) ;
2017-02-17 20:35:57 +01:00
ch_stall_mask = 0 ;
2015-09-18 00:41:14 +02:00
ch_stall_stat . data . store ( { } ) ;
ch_atomic_stat . data . store ( { } ) ;
2013-12-22 18:40:50 +01:00
2015-03-02 03:10:41 +01:00
ch_in_mbox . clear ( ) ;
2013-11-03 20:23:16 +01:00
2015-09-18 00:41:14 +02:00
ch_out_mbox . data . store ( { } ) ;
ch_out_intr_mbox . data . store ( { } ) ;
2014-07-16 18:10:18 +02:00
2015-03-13 02:59:25 +01:00
snr_config = 0 ;
2014-08-21 00:12:56 +02:00
2015-09-18 00:41:14 +02:00
ch_snr1 . data . store ( { } ) ;
ch_snr2 . data . store ( { } ) ;
2015-03-02 03:10:41 +01:00
2015-09-18 00:41:14 +02:00
ch_event_mask = 0 ;
ch_event_stat = 0 ;
2017-12-01 21:11:06 +01:00
interrupts_enabled = false ;
2017-02-17 20:35:57 +01:00
raddr = 0 ;
2015-03-02 03:10:41 +01:00
2015-07-06 01:21:15 +02:00
ch_dec_start_timestamp = get_timebased_time ( ) ; // ???
2015-03-13 02:59:25 +01:00
ch_dec_value = 0 ;
2015-03-02 03:10:41 +01:00
2015-09-18 00:41:14 +02:00
run_ctrl = 0 ;
status = 0 ;
npc = 0 ;
2015-03-02 03:10:41 +01:00
2015-09-18 00:41:14 +02:00
int_ctrl [ 0 ] . clear ( ) ;
int_ctrl [ 1 ] . clear ( ) ;
int_ctrl [ 2 ] . clear ( ) ;
2015-03-02 03:10:41 +01:00
2015-08-26 04:54:06 +02:00
gpr [ 1 ] . _u32 [ 3 ] = 0x3FFF0 ; // initial stack frame pointer
2012-11-15 00:39:56 +01:00
}
2016-04-27 00:27:24 +02:00
extern thread_local std : : string ( * g_tls_log_prefix ) ( ) ;
2016-04-14 01:09:41 +02:00
void SPUThread : : cpu_task ( )
2015-02-01 14:52:34 +01:00
{
2016-04-14 01:09:41 +02:00
std : : fesetround ( FE_TOWARDZERO ) ;
2017-12-09 15:57:43 +01:00
2018-05-12 21:55:08 +02:00
if ( g_cfg . core . set_daz_and_ftz & & g_cfg . core . spu_decoder ! = spu_decoder_type : : precise )
2018-05-09 22:35:05 +02:00
{
// Set DAZ and FTZ
_mm_setcsr ( _mm_getcsr ( ) | 0x8840 ) ;
}
2016-04-27 00:27:24 +02:00
g_tls_log_prefix = [ ]
2015-03-20 17:53:54 +01:00
{
2016-04-14 01:09:41 +02:00
const auto cpu = static_cast < SPUThread * > ( get_current_cpu_thread ( ) ) ;
2015-03-20 17:53:54 +01:00
2016-04-14 01:09:41 +02:00
return fmt : : format ( " %s [0x%05x] " , cpu - > get_name ( ) , cpu - > pc ) ;
} ;
2018-04-09 16:45:37 +02:00
if ( jit )
{
while ( LIKELY ( ! test ( state ) | | ! check_state ( ) ) )
{
jit_dispatcher [ pc / 4 ] ( * this , vm : : _ptr < u8 > ( offset ) , nullptr ) ;
}
return ;
}
2016-04-14 01:09:41 +02:00
// Select opcode table
const auto & table = * (
2017-12-09 15:57:43 +01:00
g_cfg . core . spu_decoder = = spu_decoder_type : : precise ? & g_spu_interpreter_precise . get_table ( ) :
g_cfg . core . spu_decoder = = spu_decoder_type : : fast ? & g_spu_interpreter_fast . get_table ( ) :
2016-08-08 18:01:06 +02:00
( fmt : : throw_exception < std : : logic_error > ( " Invalid SPU decoder " ) , nullptr ) ) ;
2016-04-14 01:09:41 +02:00
2018-02-27 17:03:00 +01:00
// LS pointer
const auto base = vm : : _ptr < const u8 > ( offset ) ;
const auto bswap4 = _mm_set_epi8 ( 12 , 13 , 14 , 15 , 8 , 9 , 10 , 11 , 4 , 5 , 6 , 7 , 0 , 1 , 2 , 3 ) ;
v128 _op ;
using func_t = decltype ( & spu_interpreter : : UNK ) ;
func_t func0 , func1 , func2 , func3 , func4 , func5 ;
2016-04-14 01:09:41 +02:00
while ( true )
2015-03-20 17:53:54 +01:00
{
2018-02-27 17:03:00 +01:00
if ( UNLIKELY ( test ( state ) ) )
2016-04-14 01:09:41 +02:00
{
2018-02-27 17:03:00 +01:00
if ( check_state ( ) ) return ;
2016-04-14 01:09:41 +02:00
2018-02-27 17:03:00 +01:00
// Decode single instruction (may be step)
const u32 op = * reinterpret_cast < const be_t < u32 > * > ( base + pc ) ;
if ( table [ spu_decode ( op ) ] ( * this , { op } ) ) { pc + = 4 ; }
continue ;
}
2018-02-21 11:30:41 +01:00
2018-02-27 17:03:00 +01:00
if ( pc % 16 | | ! s_use_ssse3 )
{
// Unaligned
const u32 op = * reinterpret_cast < const be_t < u32 > * > ( base + pc ) ;
if ( table [ spu_decode ( op ) ] ( * this , { op } ) ) { pc + = 4 ; }
2016-04-14 01:09:41 +02:00
continue ;
}
2018-02-27 17:03:00 +01:00
// Reinitialize
_op . vi = _mm_shuffle_epi8 ( _mm_load_si128 ( reinterpret_cast < const __m128i * > ( base + pc ) ) , bswap4 ) ;
func0 = table [ spu_decode ( _op . _u32 [ 0 ] ) ] ;
func1 = table [ spu_decode ( _op . _u32 [ 1 ] ) ] ;
func2 = table [ spu_decode ( _op . _u32 [ 2 ] ) ] ;
func3 = table [ spu_decode ( _op . _u32 [ 3 ] ) ] ;
while ( LIKELY ( func0 ( * this , { _op . _u32 [ 0 ] } ) ) )
{
pc + = 4 ;
if ( LIKELY ( func1 ( * this , { _op . _u32 [ 1 ] } ) ) )
{
pc + = 4 ;
u32 op2 = _op . _u32 [ 2 ] ;
u32 op3 = _op . _u32 [ 3 ] ;
_op . vi = _mm_shuffle_epi8 ( _mm_load_si128 ( reinterpret_cast < const __m128i * > ( base + pc + 8 ) ) , bswap4 ) ;
func0 = table [ spu_decode ( _op . _u32 [ 0 ] ) ] ;
func1 = table [ spu_decode ( _op . _u32 [ 1 ] ) ] ;
func4 = table [ spu_decode ( _op . _u32 [ 2 ] ) ] ;
func5 = table [ spu_decode ( _op . _u32 [ 3 ] ) ] ;
if ( LIKELY ( func2 ( * this , { op2 } ) ) )
{
pc + = 4 ;
if ( LIKELY ( func3 ( * this , { op3 } ) ) )
{
pc + = 4 ;
func2 = func4 ;
func3 = func5 ;
if ( UNLIKELY ( test ( state ) ) )
{
break ;
}
continue ;
}
break ;
}
break ;
}
break ;
}
2015-03-20 17:53:54 +01:00
}
2012-11-15 00:39:56 +01:00
}
2018-04-03 16:19:07 +02:00
void SPUThread : : cpu_mem ( )
{
2018-05-14 22:07:36 +02:00
//vm::passive_lock(*this);
2018-04-03 16:19:07 +02:00
}
void SPUThread : : cpu_unmem ( )
{
2018-05-14 22:07:36 +02:00
//state.test_and_set(cpu_flag::memory);
2018-04-03 16:19:07 +02:00
}
2016-04-25 12:49:12 +02:00
SPUThread : : ~ SPUThread ( )
{
// Deallocate Local Storage
vm : : dealloc_verbose_nothrow ( offset ) ;
}
2017-02-05 00:26:57 +01:00
SPUThread : : SPUThread ( const std : : string & name , u32 index , lv2_spu_group * group )
2017-01-25 18:50:30 +01:00
: cpu_thread ( idm : : last_id ( ) )
2016-06-25 15:54:08 +02:00
, m_name ( name )
2016-04-25 12:49:12 +02:00
, index ( index )
2017-03-11 00:14:48 +01:00
, offset ( 0 )
2017-02-05 00:26:57 +01:00
, group ( group )
2016-04-14 01:09:41 +02:00
{
2018-04-09 16:45:37 +02:00
if ( g_cfg . core . spu_decoder = = spu_decoder_type : : asmjit )
{
2018-05-03 14:55:45 +02:00
jit = spu_recompiler_base : : make_asmjit_recompiler ( ) ;
2018-04-09 16:45:37 +02:00
}
if ( g_cfg . core . spu_decoder = = spu_decoder_type : : llvm )
{
2018-05-03 14:55:45 +02:00
jit = spu_recompiler_base : : make_llvm_recompiler ( ) ;
2018-04-09 16:45:37 +02:00
}
2018-05-03 14:55:45 +02:00
2018-06-03 23:20:14 +02:00
if ( g_cfg . core . spu_decoder ! = spu_decoder_type : : fast & & g_cfg . core . spu_decoder ! = spu_decoder_type : : precise )
{
// Initialize lookup table
jit_dispatcher . fill ( & spu_recompiler_base : : dispatch ) ;
if ( g_cfg . core . spu_block_size ! = spu_block_size_type : : safe )
{
// Initialize stack mirror
std : : memset ( stack_mirror . data ( ) , 0xff , sizeof ( stack_mirror ) ) ;
}
}
2016-04-14 01:09:41 +02:00
}
2014-09-24 20:44:26 +02:00
2016-04-14 01:09:41 +02:00
void SPUThread : : push_snr ( u32 number , u32 value )
{
2016-04-19 15:04:02 +02:00
// Get channel
const auto channel = number & 1 ? & ch_snr2 : & ch_snr1 ;
2014-09-24 20:44:26 +02:00
2016-04-19 15:04:02 +02:00
// Check corresponding SNR register settings
2016-04-14 01:09:41 +02:00
if ( ( snr_config > > number ) & 1 )
2015-07-01 00:25:52 +02:00
{
2016-04-19 15:04:02 +02:00
channel - > push_or ( * this , value ) ;
2015-07-01 00:25:52 +02:00
}
2016-04-14 01:09:41 +02:00
else
2015-07-01 00:25:52 +02:00
{
2016-04-19 15:04:02 +02:00
channel - > push ( * this , value ) ;
2016-04-14 01:09:41 +02:00
}
2014-10-02 12:29:20 +02:00
}
2018-03-24 22:03:32 +01:00
void SPUThread : : do_dma_transfer ( const spu_mfc_cmd & args )
2014-08-22 23:15:02 +02:00
{
2018-03-10 05:16:22 +01:00
const bool is_get = ( args . cmd & ~ ( MFC_BARRIER_MASK | MFC_FENCE_MASK | MFC_START_MASK ) ) = = MFC_GET_CMD ;
2014-08-22 23:15:02 +02:00
2017-02-17 20:35:57 +01:00
u32 eal = args . eal ;
u32 lsa = args . lsa & 0x3ffff ;
2015-02-15 18:13:06 +01:00
2018-04-01 20:48:58 +02:00
// SPU Thread Group MMIO (LS and SNR) and RawSPU MMIO
if ( eal > = RAW_SPU_BASE_ADDR )
2014-08-22 23:15:02 +02:00
{
2015-03-04 22:51:14 +01:00
const u32 index = ( eal - SYS_SPU_THREAD_BASE_LOW ) / SYS_SPU_THREAD_OFFSET ; // thread number in group
const u32 offset = ( eal - SYS_SPU_THREAD_BASE_LOW ) % SYS_SPU_THREAD_OFFSET ; // LS offset or MMIO register
2015-02-15 18:13:06 +01:00
2018-04-01 20:48:58 +02:00
if ( eal < SYS_SPU_THREAD_BASE_LOW )
{
// RawSPU MMIO
auto thread = idm : : get < RawSPUThread > ( ( eal - RAW_SPU_BASE_ADDR ) / RAW_SPU_OFFSET ) ;
if ( ! thread )
{
fmt : : throw_exception ( " RawSPU not found (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE , args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
}
u32 value ;
if ( ( eal - RAW_SPU_BASE_ADDR ) % RAW_SPU_OFFSET + args . size - 1 < 0x40000 ) // LS access
{
}
else if ( args . size = = 4 & & is_get & & thread - > read_reg ( eal , value ) )
{
_ref < u32 > ( lsa ) = value ;
return ;
}
else if ( args . size = = 4 & & ! is_get & & thread - > write_reg ( eal , _ref < u32 > ( lsa ) ) )
{
return ;
}
else
{
fmt : : throw_exception ( " Invalid RawSPU MMIO offset (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE , args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
}
}
else if ( this - > offset > = RAW_SPU_BASE_ADDR )
{
fmt : : throw_exception ( " SPU MMIO used for RawSPU (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE , args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
}
else if ( group & & index < group - > num & & group - > threads [ index ] )
2015-02-15 18:13:06 +01:00
{
2015-04-12 03:36:25 +02:00
auto & spu = static_cast < SPUThread & > ( * group - > threads [ index ] ) ;
2014-09-19 02:19:22 +02:00
2015-03-02 03:10:41 +01:00
if ( offset + args . size - 1 < 0x40000 ) // LS access
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
eal = spu . offset + offset ; // redirect access
2014-08-22 23:15:02 +02:00
}
2017-02-17 20:35:57 +01:00
else if ( ! is_get & & args . size = = 4 & & ( offset = = SYS_SPU_THREAD_SNR1 | | offset = = SYS_SPU_THREAD_SNR2 ) )
2014-08-22 23:15:02 +02:00
{
2017-02-17 20:35:57 +01:00
spu . push_snr ( SYS_SPU_THREAD_SNR2 = = offset , _ref < u32 > ( lsa ) ) ;
2014-08-22 23:15:02 +02:00
return ;
}
else
{
2017-02-17 20:35:57 +01:00
fmt : : throw_exception ( " Invalid MMIO offset (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE , args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
2014-08-22 23:15:02 +02:00
}
}
else
{
2017-02-17 20:35:57 +01:00
fmt : : throw_exception ( " Invalid thread type (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE , args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
2014-08-22 23:15:02 +02:00
}
}
2017-02-17 20:35:57 +01:00
void * dst = vm : : base ( eal ) ;
void * src = vm : : base ( offset + lsa ) ;
2018-05-21 19:25:05 +02:00
if ( UNLIKELY ( ! is_get & & ! g_use_rtm ) )
{
switch ( u32 size = args . size )
{
case 1 :
{
auto & res = vm : : reservation_lock ( eal , 1 ) ;
* static_cast < u8 * > ( dst ) = * static_cast < const u8 * > ( src ) ;
res & = ~ 1ull ;
break ;
}
case 2 :
{
auto & res = vm : : reservation_lock ( eal , 2 ) ;
* static_cast < u16 * > ( dst ) = * static_cast < const u16 * > ( src ) ;
res & = ~ 1ull ;
break ;
}
case 4 :
{
auto & res = vm : : reservation_lock ( eal , 4 ) ;
* static_cast < u32 * > ( dst ) = * static_cast < const u32 * > ( src ) ;
res & = ~ 1ull ;
break ;
}
case 8 :
{
auto & res = vm : : reservation_lock ( eal , 8 ) ;
* static_cast < u64 * > ( dst ) = * static_cast < const u64 * > ( src ) ;
res & = ~ 1ull ;
break ;
}
case 16 :
{
auto & res = vm : : reservation_lock ( eal , 16 ) ;
_mm_store_si128 ( static_cast < __m128i * > ( dst ) , _mm_load_si128 ( static_cast < const __m128i * > ( src ) ) ) ;
res & = ~ 1ull ;
break ;
}
default :
{
auto * res = & vm : : reservation_lock ( eal , 16 ) ;
auto vdst = static_cast < __m128i * > ( dst ) ;
auto vsrc = static_cast < const __m128i * > ( src ) ;
for ( u32 addr = eal , end = eal + size ; ; vdst + + , vsrc + + )
{
_mm_store_si128 ( vdst , _mm_load_si128 ( vsrc ) ) ;
addr + = 16 ;
if ( addr = = end )
{
break ;
}
if ( addr % 128 )
{
continue ;
}
res - > fetch_and ( ~ 1ull ) ;
res = & vm : : reservation_lock ( addr , 16 ) ;
}
res - > fetch_and ( ~ 1ull ) ;
break ;
}
}
return ;
}
2017-02-17 20:35:57 +01:00
if ( is_get )
2014-08-22 23:15:02 +02:00
{
2017-02-17 20:35:57 +01:00
std : : swap ( dst , src ) ;
}
switch ( u32 size = args . size )
2014-08-22 23:15:02 +02:00
{
2017-02-17 20:35:57 +01:00
case 1 :
{
* static_cast < u8 * > ( dst ) = * static_cast < const u8 * > ( src ) ;
break ;
2014-08-22 23:15:02 +02:00
}
2017-02-17 20:35:57 +01:00
case 2 :
{
* static_cast < u16 * > ( dst ) = * static_cast < const u16 * > ( src ) ;
break ;
}
case 4 :
{
2018-05-21 19:25:05 +02:00
* static_cast < u32 * > ( dst ) = * static_cast < const u32 * > ( src ) ;
2017-02-17 20:35:57 +01:00
break ;
}
case 8 :
2014-08-22 23:15:02 +02:00
{
2018-05-21 19:25:05 +02:00
* static_cast < u64 * > ( dst ) = * static_cast < const u64 * > ( src ) ;
break ;
}
case 16 :
{
_mm_store_si128 ( static_cast < __m128i * > ( dst ) , _mm_load_si128 ( static_cast < const __m128i * > ( src ) ) ) ;
2017-02-17 20:35:57 +01:00
break ;
}
default :
{
auto vdst = static_cast < __m128i * > ( dst ) ;
auto vsrc = static_cast < const __m128i * > ( src ) ;
auto vcnt = size / sizeof ( __m128i ) ;
while ( vcnt > = 8 )
{
const __m128i data [ ]
{
_mm_load_si128 ( vsrc + 0 ) ,
_mm_load_si128 ( vsrc + 1 ) ,
_mm_load_si128 ( vsrc + 2 ) ,
_mm_load_si128 ( vsrc + 3 ) ,
_mm_load_si128 ( vsrc + 4 ) ,
_mm_load_si128 ( vsrc + 5 ) ,
_mm_load_si128 ( vsrc + 6 ) ,
_mm_load_si128 ( vsrc + 7 ) ,
} ;
2018-05-21 19:25:05 +02:00
_mm_store_si128 ( vdst + 0 , data [ 0 ] ) ;
_mm_store_si128 ( vdst + 1 , data [ 1 ] ) ;
_mm_store_si128 ( vdst + 2 , data [ 2 ] ) ;
_mm_store_si128 ( vdst + 3 , data [ 3 ] ) ;
_mm_store_si128 ( vdst + 4 , data [ 4 ] ) ;
_mm_store_si128 ( vdst + 5 , data [ 5 ] ) ;
_mm_store_si128 ( vdst + 6 , data [ 6 ] ) ;
_mm_store_si128 ( vdst + 7 , data [ 7 ] ) ;
2017-02-17 20:35:57 +01:00
vcnt - = 8 ;
vsrc + = 8 ;
vdst + = 8 ;
}
while ( vcnt - - )
{
2018-05-21 19:25:05 +02:00
_mm_store_si128 ( vdst + + , _mm_load_si128 ( vsrc + + ) ) ;
2017-02-17 20:35:57 +01:00
}
2018-05-21 19:25:05 +02:00
break ;
2014-08-22 23:15:02 +02:00
}
}
2015-03-02 03:10:41 +01:00
}
2014-08-22 23:15:02 +02:00
2018-03-24 22:03:32 +01:00
bool SPUThread : : do_dma_check ( const spu_mfc_cmd & args )
2014-08-22 23:15:02 +02:00
{
2018-04-03 15:09:43 +02:00
const u32 mask = 1u < < args . tag ;
if ( UNLIKELY ( mfc_barrier & mask | | ( args . cmd & MFC_FENCE_MASK & & mfc_fence & mask ) ) )
2018-03-24 22:03:32 +01:00
{
2018-04-03 15:09:43 +02:00
// Check for special value combination (normally impossible)
2018-04-28 19:11:16 +02:00
if ( false )
2018-03-24 22:03:32 +01:00
{
2018-04-03 15:09:43 +02:00
// Update barrier/fence masks if necessary
mfc_barrier = 0 ;
mfc_fence = 0 ;
2018-03-24 22:03:32 +01:00
2018-04-03 15:09:43 +02:00
for ( u32 i = 0 ; i < mfc_size ; i + + )
{
2018-04-03 21:42:47 +02:00
if ( ( mfc_queue [ i ] . cmd & ~ 0xc ) = = MFC_BARRIER_CMD )
2018-04-03 15:09:43 +02:00
{
mfc_barrier | = - 1 ;
continue ;
}
2018-04-03 21:42:47 +02:00
if ( true )
2018-04-03 15:09:43 +02:00
{
const u32 _mask = 1u < < mfc_queue [ i ] . tag ;
// A command with barrier hard blocks that tag until it's been dealt with
if ( mfc_queue [ i ] . cmd & MFC_BARRIER_MASK )
{
mfc_barrier | = _mask ;
}
// A new command that has a fence can't be executed until the stalled list has been dealt with
mfc_fence | = _mask ;
}
}
if ( mfc_barrier & mask | | ( args . cmd & MFC_FENCE_MASK & & mfc_fence & mask ) )
2018-03-24 22:03:32 +01:00
{
return false ;
}
2018-04-03 15:09:43 +02:00
return true ;
2018-03-24 22:03:32 +01:00
}
2018-04-03 15:09:43 +02:00
return false ;
2018-03-24 22:03:32 +01:00
}
2015-03-02 03:10:41 +01:00
2018-03-24 22:03:32 +01:00
return true ;
}
bool SPUThread : : do_list_transfer ( spu_mfc_cmd & args )
{
struct list_element
{
be_t < u16 > sb ; // Stall-and-Notify bit (0x8000)
be_t < u16 > ts ; // List Transfer Size
be_t < u32 > ea ; // External Address Low
} item { } ;
2014-08-22 23:15:02 +02:00
2018-03-24 22:03:32 +01:00
while ( args . size )
2014-08-22 23:15:02 +02:00
{
2018-03-24 22:03:32 +01:00
if ( UNLIKELY ( item . sb & 0x8000 ) )
2017-02-17 20:35:57 +01:00
{
2018-03-24 22:03:32 +01:00
ch_stall_mask | = ( 1u < < args . tag ) ;
if ( ! ch_stall_stat . get_count ( ) )
2017-06-25 22:13:48 +02:00
{
2018-03-24 22:03:32 +01:00
ch_event_stat | = SPU_EVENT_SN ;
2017-06-25 22:13:48 +02:00
}
2014-08-22 23:15:02 +02:00
2018-03-24 22:03:32 +01:00
ch_stall_stat . set_value ( ( 1u < < args . tag ) | ch_stall_stat . get_value ( ) ) ;
return false ;
2017-06-25 22:13:48 +02:00
}
2017-02-17 20:35:57 +01:00
2018-03-24 22:03:32 +01:00
args . lsa & = 0x3fff0 ;
item = _ref < list_element > ( args . eal & 0x3fff8 ) ;
const u32 size = item . ts ;
const u32 addr = item . ea ;
LOG_TRACE ( SPU , " LIST: addr=0x%x, size=0x%x, lsa=0x%05x, sb=0x%x " , addr , size , args . lsa | ( addr & 0xf ) , item . sb ) ;
if ( size )
{
spu_mfc_cmd transfer ;
transfer . eal = addr ;
transfer . eah = 0 ;
transfer . lsa = args . lsa | ( addr & 0xf ) ;
transfer . tag = args . tag ;
transfer . cmd = MFC ( args . cmd & ~ MFC_LIST_MASK ) ;
transfer . size = size ;
do_dma_transfer ( transfer ) ;
const u32 add_size = std : : max < u32 > ( size , 16 ) ;
args . lsa + = add_size ;
}
args . eal + = 8 ;
args . size - = 8 ;
}
return true ;
}
2018-04-03 21:42:47 +02:00
void SPUThread : : do_putlluc ( const spu_mfc_cmd & args )
2018-03-24 22:03:32 +01:00
{
2018-04-03 21:42:47 +02:00
if ( raddr & & args . eal = = raddr )
{
ch_event_stat | = SPU_EVENT_LR ;
raddr = 0 ;
}
2018-03-24 22:03:32 +01:00
const u32 addr = args . eal ;
auto & data = vm : : _ref < decltype ( rdata ) > ( addr ) ;
const auto to_write = _ref < decltype ( rdata ) > ( args . lsa & 0x3ffff ) ;
// Store unconditionally
2018-05-21 19:25:05 +02:00
if ( LIKELY ( g_use_rtm ) )
2018-03-24 22:03:32 +01:00
{
2018-05-19 22:14:02 +02:00
const u64 count = spu_putlluc_tx ( addr , to_write . data ( ) ) ;
if ( count > 5 )
{
LOG_ERROR ( SPU , " %s took too long: %u " , args . cmd , count ) ;
}
2018-05-21 19:25:05 +02:00
}
else
{
auto & res = vm : : reservation_lock ( addr , 128 ) ;
2018-06-02 12:43:22 +02:00
vm : : _ref < atomic_t < u32 > > ( addr ) + = 0 ;
// Full lock (heavyweight)
// TODO: vm::check_addr
vm : : writer_lock lock ( 1 ) ;
2018-05-21 19:25:05 +02:00
data = to_write ;
vm : : reservation_update ( addr , 128 ) ;
2018-03-24 22:03:32 +01:00
}
2018-05-14 22:07:36 +02:00
vm : : reservation_notifier ( addr , 128 ) . notify_all ( ) ;
2018-03-24 22:03:32 +01:00
}
2018-04-03 21:42:47 +02:00
void SPUThread : : do_mfc ( bool wait )
2018-03-24 22:03:32 +01:00
{
u32 removed = 0 ;
u32 barrier = 0 ;
u32 fence = 0 ;
// Process enqueued commands
std : : remove_if ( mfc_queue + 0 , mfc_queue + mfc_size , [ & ] ( spu_mfc_cmd & args )
{
2018-04-03 21:42:47 +02:00
if ( ( args . cmd & ~ 0xc ) = = MFC_BARRIER_CMD )
2018-03-24 22:03:32 +01:00
{
if ( & args - mfc_queue < = removed )
{
// Remove barrier-class command if it's the first in the queue
_mm_mfence ( ) ;
removed + + ;
return true ;
}
2018-04-03 21:42:47 +02:00
// Block all tags
barrier | = - 1 ;
2018-03-24 22:03:32 +01:00
return false ;
}
// Select tag bit in the tag mask or the stall mask
const u32 mask = 1u < < args . tag ;
if ( barrier & mask )
{
2018-04-03 15:09:43 +02:00
fence | = mask ;
2018-03-24 22:03:32 +01:00
return false ;
}
2018-04-03 21:42:47 +02:00
if ( args . cmd & MFC_FENCE_MASK & & fence & mask )
{
return false ;
}
2018-03-24 22:03:32 +01:00
if ( args . cmd & MFC_LIST_MASK )
{
2018-04-03 21:42:47 +02:00
if ( ! test ( ch_stall_mask , mask ) )
2018-03-24 22:03:32 +01:00
{
2018-05-23 10:55:15 +02:00
if ( do_list_transfer ( args ) )
2018-04-03 21:42:47 +02:00
{
2018-05-23 10:55:15 +02:00
removed + + ;
return true ;
2018-04-03 21:42:47 +02:00
}
2018-03-24 22:03:32 +01:00
}
if ( args . cmd & MFC_BARRIER_MASK )
{
barrier | = mask ;
}
2018-04-03 15:09:43 +02:00
fence | = mask ;
2018-03-24 22:03:32 +01:00
return false ;
}
2018-04-03 21:42:47 +02:00
if ( args . size )
2018-03-24 22:03:32 +01:00
{
2018-05-23 10:55:15 +02:00
do_dma_transfer ( args ) ;
2018-03-24 22:03:32 +01:00
}
2018-04-03 21:42:47 +02:00
else if ( args . cmd = = MFC_PUTQLLUC_CMD )
2018-03-24 22:03:32 +01:00
{
2018-04-28 19:11:16 +02:00
if ( fence & mask )
{
return false ;
}
2018-04-03 21:42:47 +02:00
do_putlluc ( args ) ;
2018-03-24 22:03:32 +01:00
}
removed + + ;
return true ;
} ) ;
mfc_size - = removed ;
2018-04-03 15:09:43 +02:00
mfc_barrier = barrier ;
mfc_fence = fence ;
2018-03-24 22:03:32 +01:00
if ( removed & & ch_tag_upd )
{
const u32 completed = get_mfc_completed ( ) ;
if ( completed & & ch_tag_upd = = 1 )
{
ch_tag_stat . set_value ( completed ) ;
ch_tag_upd = 0 ;
}
else if ( completed = = ch_tag_mask & & ch_tag_upd = = 2 )
{
ch_tag_stat . set_value ( completed ) ;
ch_tag_upd = 0 ;
}
}
}
u32 SPUThread : : get_mfc_completed ( )
{
2018-04-03 21:42:47 +02:00
return ch_tag_mask & ~ mfc_fence ;
2018-03-24 22:03:32 +01:00
}
bool SPUThread : : process_mfc_cmd ( spu_mfc_cmd args )
{
// Stall infinitely if MFC queue is full
2018-04-03 21:42:47 +02:00
while ( UNLIKELY ( mfc_size > = 16 ) )
2018-03-24 22:03:32 +01:00
{
if ( test ( state , cpu_flag : : stop ) )
{
return false ;
}
thread_ctrl : : wait ( ) ;
}
spu : : scheduler : : concurrent_execution_watchdog watchdog ( * this ) ;
LOG_TRACE ( SPU , " DMAC: cmd=%s, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x " , args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
switch ( args . cmd )
2017-02-17 20:35:57 +01:00
{
case MFC_GETLLAR_CMD :
2014-08-22 23:15:02 +02:00
{
2018-03-24 22:03:32 +01:00
auto & data = vm : : _ref < decltype ( rdata ) > ( args . eal ) ;
2017-02-17 20:35:57 +01:00
2018-03-24 22:03:32 +01:00
if ( raddr & & raddr ! = args . eal )
2017-02-17 20:35:57 +01:00
{
ch_event_stat | = SPU_EVENT_LR ;
}
2018-04-03 21:42:47 +02:00
raddr = args . eal ;
2014-08-22 23:15:02 +02:00
2018-04-03 21:42:47 +02:00
const bool is_polling = false ; // TODO
2015-03-02 03:10:41 +01:00
2017-02-17 20:35:57 +01:00
if ( is_polling )
2015-01-13 15:54:36 +01:00
{
2018-05-14 22:07:36 +02:00
rtime = vm : : reservation_acquire ( raddr , 128 ) ;
2017-02-17 20:35:57 +01:00
2018-06-02 12:45:28 +02:00
while ( rdata = = data & & vm : : reservation_acquire ( raddr , 128 ) = = rtime )
2017-02-17 20:35:57 +01:00
{
if ( test ( state , cpu_flag : : stop ) )
{
break ;
}
2014-09-24 20:44:26 +02:00
2017-02-17 20:35:57 +01:00
thread_ctrl : : wait_for ( 100 ) ;
}
}
2018-04-03 21:42:47 +02:00
2018-05-21 19:25:05 +02:00
if ( LIKELY ( g_use_rtm ) )
2018-05-14 22:07:36 +02:00
{
2018-06-02 12:45:28 +02:00
u64 count = 0 ;
if ( g_cfg . core . spu_accurate_getllar )
{
count = spu_getll_tx ( raddr , rdata . data ( ) , & rtime ) ;
}
if ( count = = 0 )
{
for ( + + count ; ; count + + , busy_wait ( 300 ) )
{
rtime = vm : : reservation_acquire ( raddr , 128 ) ;
rdata = data ;
if ( LIKELY ( vm : : reservation_acquire ( raddr , 128 ) = = rtime ) )
{
break ;
}
}
}
2018-05-19 22:20:41 +02:00
if ( count > 9 )
{
LOG_ERROR ( SPU , " %s took too long: %u " , args . cmd , count ) ;
}
2018-05-14 22:07:36 +02:00
}
2018-05-21 19:25:05 +02:00
else
2017-02-17 20:35:57 +01:00
{
2018-05-21 19:25:05 +02:00
auto & res = vm : : reservation_lock ( raddr , 128 ) ;
2018-06-02 12:45:28 +02:00
if ( g_cfg . core . spu_accurate_getllar )
{
vm : : _ref < atomic_t < u32 > > ( raddr ) + = 0 ;
// Full lock (heavyweight)
// TODO: vm::check_addr
vm : : writer_lock lock ( 1 ) ;
rtime = res & ~ 1ull ;
rdata = data ;
res & = ~ 1ull ;
}
else
{
rtime = res & ~ 1ull ;
rdata = data ;
res & = ~ 1ull ;
}
2015-01-13 15:54:36 +01:00
}
2014-08-22 23:15:02 +02:00
2017-02-17 20:35:57 +01:00
// Copy to LS
2018-03-24 22:03:32 +01:00
_ref < decltype ( rdata ) > ( args . lsa & 0x3ffff ) = rdata ;
ch_atomic_stat . set_value ( MFC_GETLLAR_SUCCESS ) ;
return true ;
2017-02-17 20:35:57 +01:00
}
case MFC_PUTLLC_CMD :
{
// Store conditionally
2018-03-24 22:03:32 +01:00
auto & data = vm : : _ref < decltype ( rdata ) > ( args . eal ) ;
const auto to_write = _ref < decltype ( rdata ) > ( args . lsa & 0x3ffff ) ;
2017-02-17 20:35:57 +01:00
bool result = false ;
2018-04-03 21:42:47 +02:00
if ( raddr = = args . eal & & rtime = = vm : : reservation_acquire ( raddr , 128 ) )
2014-08-22 23:15:02 +02:00
{
2018-05-21 19:25:05 +02:00
if ( LIKELY ( g_use_rtm ) )
2017-07-18 19:03:47 +02:00
{
2018-05-18 17:51:48 +02:00
if ( spu_putllc_tx ( raddr , rtime , rdata . data ( ) , to_write . data ( ) ) )
2017-07-18 19:03:47 +02:00
{
2018-05-18 17:51:48 +02:00
vm : : reservation_notifier ( raddr , 128 ) . notify_all ( ) ;
result = true ;
2018-04-03 21:42:47 +02:00
}
2018-05-18 17:51:48 +02:00
// Don't fallback to heavyweight lock, just give up
2018-04-03 21:42:47 +02:00
}
else if ( rdata = = data )
{
2018-05-21 19:25:05 +02:00
auto & res = vm : : reservation_lock ( raddr , 128 ) ;
vm : : _ref < atomic_t < u32 > > ( raddr ) + = 0 ;
2018-04-03 21:42:47 +02:00
// Full lock (heavyweight)
2018-05-14 22:07:36 +02:00
// TODO: vm::check_addr
2018-04-03 21:42:47 +02:00
vm : : writer_lock lock ( 1 ) ;
2017-02-17 20:35:57 +01:00
2018-05-21 19:25:05 +02:00
if ( rtime = = ( res & ~ 1ull ) & & rdata = = data )
2017-07-18 19:03:47 +02:00
{
data = to_write ;
vm : : reservation_update ( raddr , 128 ) ;
2018-05-14 22:07:36 +02:00
vm : : reservation_notifier ( raddr , 128 ) . notify_all ( ) ;
2018-05-21 19:25:05 +02:00
result = true ;
}
else
{
res & = ~ 1ull ;
2017-07-18 19:03:47 +02:00
}
2017-02-17 20:35:57 +01:00
}
}
if ( result )
{
ch_atomic_stat . set_value ( MFC_PUTLLC_SUCCESS ) ;
2014-08-22 23:15:02 +02:00
}
2017-02-17 20:35:57 +01:00
else
{
ch_atomic_stat . set_value ( MFC_PUTLLC_FAILURE ) ;
}
if ( raddr & & ! result )
{
ch_event_stat | = SPU_EVENT_LR ;
}
raddr = 0 ;
2018-03-24 22:03:32 +01:00
return true ;
2014-08-22 23:15:02 +02:00
}
2017-02-17 20:35:57 +01:00
case MFC_PUTLLUC_CMD :
{
2018-04-03 21:42:47 +02:00
do_putlluc ( args ) ;
2017-02-17 20:35:57 +01:00
ch_atomic_stat . set_value ( MFC_PUTLLUC_SUCCESS ) ;
2018-03-24 22:03:32 +01:00
return true ;
2017-02-17 20:35:57 +01:00
}
case MFC_PUTQLLUC_CMD :
2014-08-22 23:15:02 +02:00
{
2018-04-28 19:11:16 +02:00
const u32 mask = 1u < < args . tag ;
if ( UNLIKELY ( ( mfc_barrier | mfc_fence ) & mask ) )
2018-03-24 22:03:32 +01:00
{
2018-04-03 21:42:47 +02:00
args . size = 0 ;
2018-03-24 22:03:32 +01:00
mfc_queue [ mfc_size + + ] = args ;
2018-04-28 19:11:16 +02:00
mfc_fence | = mask ;
2018-04-03 21:42:47 +02:00
}
else
{
do_putlluc ( args ) ;
2018-03-24 22:03:32 +01:00
}
return true ;
2017-02-17 20:35:57 +01:00
}
case MFC_SNDSIG_CMD :
case MFC_SNDSIGB_CMD :
case MFC_SNDSIGF_CMD :
{
2018-03-24 22:03:32 +01:00
args . size = 4 ;
2017-02-17 20:35:57 +01:00
// Fallthrough
}
2014-08-22 23:15:02 +02:00
case MFC_PUT_CMD :
2015-03-02 03:10:41 +01:00
case MFC_PUTB_CMD :
case MFC_PUTF_CMD :
case MFC_PUTR_CMD :
case MFC_PUTRB_CMD :
case MFC_PUTRF_CMD :
2014-08-22 23:15:02 +02:00
case MFC_GET_CMD :
2015-03-02 03:10:41 +01:00
case MFC_GETB_CMD :
case MFC_GETF_CMD :
2014-08-22 23:15:02 +02:00
{
2018-03-24 22:03:32 +01:00
if ( LIKELY ( args . size < = 0x4000 ) )
2017-02-17 20:35:57 +01:00
{
2018-04-03 21:42:47 +02:00
if ( LIKELY ( do_dma_check ( args ) ) )
2017-02-17 20:35:57 +01:00
{
2018-04-03 21:42:47 +02:00
if ( LIKELY ( args . size ) )
{
2018-05-23 10:55:15 +02:00
do_dma_transfer ( args ) ;
2018-04-03 21:42:47 +02:00
}
2018-05-23 10:55:15 +02:00
return true ;
2017-02-17 20:35:57 +01:00
}
2014-08-22 23:15:02 +02:00
2018-04-03 21:42:47 +02:00
mfc_queue [ mfc_size + + ] = args ;
mfc_fence | = 1u < < args . tag ;
if ( args . cmd & MFC_BARRIER_MASK )
2017-02-17 20:35:57 +01:00
{
2018-04-03 21:42:47 +02:00
mfc_barrier | = 1u < < args . tag ;
2017-02-17 20:35:57 +01:00
}
2018-03-24 22:03:32 +01:00
return true ;
2017-02-17 20:35:57 +01:00
}
2017-12-09 15:57:43 +01:00
2017-02-17 20:35:57 +01:00
break ;
}
2014-08-22 23:15:02 +02:00
case MFC_PUTL_CMD :
2015-03-02 03:10:41 +01:00
case MFC_PUTLB_CMD :
case MFC_PUTLF_CMD :
case MFC_PUTRL_CMD :
case MFC_PUTRLB_CMD :
case MFC_PUTRLF_CMD :
2014-08-22 23:15:02 +02:00
case MFC_GETL_CMD :
2015-03-02 03:10:41 +01:00
case MFC_GETLB_CMD :
case MFC_GETLF_CMD :
2014-08-22 23:15:02 +02:00
{
2018-03-24 22:03:32 +01:00
if ( LIKELY ( args . size < = 0x4000 ) )
2015-07-21 22:14:04 +02:00
{
2018-04-03 21:42:47 +02:00
if ( LIKELY ( do_dma_check ( args ) & & ! test ( ch_stall_mask , 1u < < args . tag ) ) )
{
2018-05-23 10:55:15 +02:00
if ( LIKELY ( do_list_transfer ( args ) ) )
2018-04-03 21:42:47 +02:00
{
2018-05-23 10:55:15 +02:00
return true ;
2018-04-03 21:42:47 +02:00
}
}
mfc_queue [ mfc_size + + ] = args ;
mfc_fence | = 1u < < args . tag ;
if ( args . cmd & MFC_BARRIER_MASK )
2015-07-21 22:14:04 +02:00
{
2018-04-03 21:42:47 +02:00
mfc_barrier | = 1u < < args . tag ;
2015-07-21 22:14:04 +02:00
}
2018-03-24 22:03:32 +01:00
return true ;
2015-03-02 03:10:41 +01:00
}
2017-02-17 20:35:57 +01:00
break ;
2014-08-22 23:15:02 +02:00
}
2015-12-20 09:16:31 +01:00
case MFC_BARRIER_CMD :
case MFC_EIEIO_CMD :
2018-04-01 19:52:54 +02:00
case MFC_SYNC_CMD :
{
2018-04-03 21:42:47 +02:00
if ( mfc_size = = 0 )
2018-04-01 19:52:54 +02:00
{
_mm_mfence ( ) ;
}
else
{
mfc_queue [ mfc_size + + ] = args ;
2018-04-03 21:42:47 +02:00
mfc_barrier | = - 1 ;
2018-04-01 19:52:54 +02:00
}
return true ;
}
2017-02-17 20:35:57 +01:00
default :
{
2018-03-24 22:03:32 +01:00
break ;
2017-02-17 20:35:57 +01:00
}
2015-02-16 02:53:53 +01:00
}
2015-03-02 03:10:41 +01:00
2018-03-24 22:03:32 +01:00
fmt : : throw_exception ( " Unknown command (cmd=%s, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE ,
args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
2014-08-22 23:15:02 +02:00
}
2015-07-15 20:11:32 +02:00
u32 SPUThread : : get_events ( bool waiting )
2015-07-21 22:14:04 +02:00
{
2017-02-17 20:35:57 +01:00
// Check reservation status and set SPU_EVENT_LR if lost
2018-02-09 15:49:37 +01:00
if ( raddr & & ( vm : : reservation_acquire ( raddr , sizeof ( rdata ) ) ! = rtime | | rdata ! = vm : : _ref < decltype ( rdata ) > ( raddr ) ) )
2015-07-21 22:14:04 +02:00
{
ch_event_stat | = SPU_EVENT_LR ;
2017-02-17 20:35:57 +01:00
raddr = 0 ;
2015-07-15 20:11:32 +02:00
}
2017-02-07 21:59:59 +01:00
// SPU Decrementer Event
2017-02-13 14:12:24 +01:00
if ( ! ch_dec_value | | ( ch_dec_value - ( get_timebased_time ( ) - ch_dec_start_timestamp ) ) > > 31 )
2017-02-07 21:59:59 +01:00
{
2017-02-13 14:12:24 +01:00
if ( ( ch_event_stat & SPU_EVENT_TM ) = = 0 )
{
ch_event_stat | = SPU_EVENT_TM ;
}
2017-02-07 21:59:59 +01:00
}
2017-02-17 20:35:57 +01:00
// Simple polling or polling with atomically set/removed SPU_EVENT_WAITING flag
return ! waiting ? ch_event_stat & ch_event_mask : ch_event_stat . atomic_op ( [ & ] ( u32 & stat ) - > u32
2015-07-15 20:11:32 +02:00
{
2017-02-17 20:35:57 +01:00
if ( u32 res = stat & ch_event_mask )
2015-07-15 20:11:32 +02:00
{
2017-02-17 20:35:57 +01:00
stat & = ~ SPU_EVENT_WAITING ;
return res ;
}
2015-07-21 22:14:04 +02:00
2017-02-17 20:35:57 +01:00
stat | = SPU_EVENT_WAITING ;
return 0 ;
} ) ;
2015-07-21 22:14:04 +02:00
}
2015-07-15 20:11:32 +02:00
void SPUThread : : set_events ( u32 mask )
{
if ( u32 unimpl = mask & ~ SPU_EVENT_IMPLEMENTED )
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Unimplemented events (0x%x) " HERE , unimpl ) ;
2015-07-15 20:11:32 +02:00
}
2016-05-13 15:55:34 +02:00
// Set new events, get old event mask
2016-04-14 01:09:41 +02:00
const u32 old_stat = ch_event_stat . fetch_or ( mask ) ;
2015-07-15 20:11:32 +02:00
2016-05-13 15:55:34 +02:00
// Notify if some events were set
if ( ~ old_stat & mask & & old_stat & SPU_EVENT_WAITING & & ch_event_stat & SPU_EVENT_WAITING )
2015-07-15 20:11:32 +02:00
{
2016-09-07 00:38:52 +02:00
notify ( ) ;
2015-07-15 20:11:32 +02:00
}
}
2015-07-16 13:32:19 +02:00
void SPUThread : : set_interrupt_status ( bool enable )
{
if ( enable )
{
// detect enabling interrupts with events masked
2017-02-13 14:12:24 +01:00
if ( u32 mask = ch_event_mask & ~ SPU_EVENT_INTR_IMPLEMENTED )
2015-07-16 13:32:19 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " SPU Interrupts not implemented (mask=0x%x) " HERE , mask ) ;
2015-07-16 13:32:19 +02:00
}
2017-12-01 21:11:06 +01:00
interrupts_enabled = true ;
2015-07-16 13:32:19 +02:00
}
else
{
2017-12-01 21:11:06 +01:00
interrupts_enabled = false ;
2015-07-16 13:32:19 +02:00
}
}
2015-03-02 03:10:41 +01:00
u32 SPUThread : : get_ch_count ( u32 ch )
2014-08-22 23:15:02 +02:00
{
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " get_ch_count(ch=%d [%s]) " , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " ) ;
2015-03-02 03:10:41 +01:00
switch ( ch )
{
2017-02-17 20:35:57 +01:00
case SPU_WrOutMbox : return ch_out_mbox . get_count ( ) ^ 1 ;
case SPU_WrOutIntrMbox : return ch_out_intr_mbox . get_count ( ) ^ 1 ;
case SPU_RdInMbox : return ch_in_mbox . get_count ( ) ;
case MFC_RdTagStat : return ch_tag_stat . get_count ( ) ;
case MFC_RdListStallStat : return ch_stall_stat . get_count ( ) ;
case MFC_WrTagUpdate : return ch_tag_upd = = 0 ;
case SPU_RdSigNotify1 : return ch_snr1 . get_count ( ) ;
case SPU_RdSigNotify2 : return ch_snr2 . get_count ( ) ;
case MFC_RdAtomicStat : return ch_atomic_stat . get_count ( ) ;
case SPU_RdEventStat : return get_events ( ) ! = 0 ;
2018-03-24 22:03:32 +01:00
case MFC_Cmd : return 16 - mfc_size ;
2015-03-02 03:10:41 +01:00
}
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Unknown/illegal channel (ch=%d [%s]) " HERE , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " ) ;
2014-08-22 23:15:02 +02:00
}
2018-04-30 18:39:06 +02:00
s64 SPUThread : : get_ch_value ( u32 ch )
2014-08-22 23:15:02 +02:00
{
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " get_ch_value(ch=%d [%s]) " , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " ) ;
2014-10-02 12:29:20 +02:00
2018-04-30 18:39:06 +02:00
auto read_channel = [ & ] ( spu_channel & channel ) - > s64
2015-07-03 18:07:36 +02:00
{
2017-02-17 20:35:57 +01:00
for ( int i = 0 ; i < 10 & & channel . get_count ( ) = = 0 ; i + + )
{
2018-05-14 22:07:36 +02:00
busy_wait ( ) ;
2017-02-17 20:35:57 +01:00
}
2018-04-30 18:39:06 +02:00
u32 out ;
2017-02-17 20:35:57 +01:00
while ( ! channel . try_pop ( out ) )
2015-07-03 18:07:36 +02:00
{
2017-02-17 20:35:57 +01:00
if ( test ( state , cpu_flag : : stop ) )
{
2018-04-30 18:39:06 +02:00
return - 1 ;
2017-02-17 20:35:57 +01:00
}
2015-07-17 18:27:12 +02:00
2018-03-24 22:03:32 +01:00
thread_ctrl : : wait ( ) ;
2015-07-03 18:07:36 +02:00
}
2016-05-13 15:55:34 +02:00
2018-04-30 18:39:06 +02:00
return out ;
2015-07-03 18:07:36 +02:00
} ;
2014-08-22 23:15:02 +02:00
switch ( ch )
{
2017-02-13 14:12:24 +01:00
case SPU_RdSRR0 :
{
2018-04-30 18:39:06 +02:00
return srr0 ;
2017-02-13 14:12:24 +01:00
}
2015-03-02 03:10:41 +01:00
case SPU_RdInMbox :
{
2015-07-17 18:27:12 +02:00
while ( true )
2015-03-02 03:10:41 +01:00
{
2017-02-17 20:35:57 +01:00
for ( int i = 0 ; i < 10 & & ch_in_mbox . get_count ( ) = = 0 ; i + + )
{
2018-05-14 22:07:36 +02:00
busy_wait ( ) ;
2017-02-17 20:35:57 +01:00
}
2018-04-30 18:39:06 +02:00
u32 out ;
2016-04-19 15:04:02 +02:00
if ( const uint old_count = ch_in_mbox . try_pop ( out ) )
2015-07-17 18:27:12 +02:00
{
2016-04-19 15:04:02 +02:00
if ( old_count = = 4 /* SPU_IN_MBOX_THRESHOLD */ ) // TODO: check this
2015-07-17 18:27:12 +02:00
{
int_ctrl [ 2 ] . set ( SPU_INT2_STAT_SPU_MAILBOX_THRESHOLD_INT ) ;
}
2018-04-30 18:39:06 +02:00
return out ;
2015-07-17 18:27:12 +02:00
}
2016-08-09 16:14:41 +02:00
if ( test ( state & cpu_flag : : stop ) )
2016-04-14 01:09:41 +02:00
{
2018-04-30 18:39:06 +02:00
return - 1 ;
2016-04-14 01:09:41 +02:00
}
2015-07-03 18:07:36 +02:00
2016-07-27 23:43:22 +02:00
thread_ctrl : : wait ( ) ;
2015-03-02 03:10:41 +01:00
}
}
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
case MFC_RdTagStat :
{
2018-03-24 22:03:32 +01:00
if ( ch_tag_stat . get_count ( ) )
{
2018-04-30 18:39:06 +02:00
u32 out = ch_tag_stat . get_value ( ) ;
2018-03-24 22:03:32 +01:00
ch_tag_stat . set_value ( 0 , false ) ;
2018-04-30 18:39:06 +02:00
return out ;
2018-03-24 22:03:32 +01:00
}
// Will stall infinitely
2015-07-03 18:07:36 +02:00
return read_channel ( ch_tag_stat ) ;
2015-03-02 03:10:41 +01:00
}
case MFC_RdTagMask :
{
2018-04-30 18:39:06 +02:00
return ch_tag_mask ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdSigNotify1 :
{
2015-07-03 18:07:36 +02:00
return read_channel ( ch_snr1 ) ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdSigNotify2 :
{
2015-07-03 18:07:36 +02:00
return read_channel ( ch_snr2 ) ;
2015-03-02 03:10:41 +01:00
}
case MFC_RdAtomicStat :
{
2018-03-24 22:03:32 +01:00
if ( ch_atomic_stat . get_count ( ) )
{
2018-04-30 18:39:06 +02:00
u32 out = ch_atomic_stat . get_value ( ) ;
2018-03-24 22:03:32 +01:00
ch_atomic_stat . set_value ( 0 , false ) ;
2018-04-30 18:39:06 +02:00
return out ;
2018-03-24 22:03:32 +01:00
}
// Will stall infinitely
2015-07-03 18:07:36 +02:00
return read_channel ( ch_atomic_stat ) ;
2015-03-02 03:10:41 +01:00
}
case MFC_RdListStallStat :
{
2018-03-24 22:03:32 +01:00
if ( ch_stall_stat . get_count ( ) )
{
2018-04-30 18:39:06 +02:00
u32 out = ch_stall_stat . get_value ( ) ;
2018-03-24 22:03:32 +01:00
ch_stall_stat . set_value ( 0 , false ) ;
2018-04-30 18:39:06 +02:00
return out ;
2018-03-24 22:03:32 +01:00
}
// Will stall infinitely
2015-07-03 18:07:36 +02:00
return read_channel ( ch_stall_stat ) ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdDec :
{
2018-04-30 18:39:06 +02:00
u32 out = ch_dec_value - ( u32 ) ( get_timebased_time ( ) - ch_dec_start_timestamp ) ;
2017-07-14 16:00:49 +02:00
//Polling: We might as well hint to the scheduler to slot in another thread since this one is counting down
if ( g_cfg . core . spu_loop_detection & & out > spu : : scheduler : : native_jiffy_duration_us )
std : : this_thread : : yield ( ) ;
2018-04-30 18:39:06 +02:00
return out ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdEventMask :
{
2018-04-30 18:39:06 +02:00
return ch_event_mask ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdEventStat :
{
2017-02-17 20:35:57 +01:00
u32 res = get_events ( ) ;
if ( res )
2015-03-02 03:10:41 +01:00
{
2018-04-30 18:39:06 +02:00
return res ;
2015-07-15 20:11:32 +02:00
}
2015-07-04 01:22:24 +02:00
2018-05-14 22:07:36 +02:00
const u32 mask1 = ch_event_mask ;
2017-02-17 20:35:57 +01:00
2018-05-14 22:07:36 +02:00
if ( mask1 & SPU_EVENT_LR & & raddr )
2015-07-15 20:11:32 +02:00
{
2018-05-17 18:34:35 +02:00
if ( mask1 ! = SPU_EVENT_LR & & mask1 ! = SPU_EVENT_LR + SPU_EVENT_TM )
2018-05-14 22:07:36 +02:00
{
2018-05-17 18:34:35 +02:00
// Combining LR with other flags needs another solution
2018-05-14 22:07:36 +02:00
fmt : : throw_exception ( " Not supported: event mask 0x%x " HERE , mask1 ) ;
}
2018-05-18 22:19:44 +02:00
std : : shared_lock < notifier > pseudo_lock ( vm : : reservation_notifier ( raddr , 128 ) , std : : try_to_lock ) ;
verify ( HERE ) , pseudo_lock ;
2018-05-14 22:07:36 +02:00
while ( res = get_events ( ) , ! res )
{
if ( test ( state , cpu_flag : : stop + cpu_flag : : dbg_global_stop ) )
{
return - 1 ;
}
pseudo_lock . mutex ( ) - > wait ( 100 ) ;
}
return res ;
2015-07-15 20:11:32 +02:00
}
2017-02-17 20:35:57 +01:00
2018-05-14 22:07:36 +02:00
while ( res = get_events ( true ) , ! res )
2015-07-15 20:11:32 +02:00
{
2017-02-17 20:35:57 +01:00
if ( test ( state & cpu_flag : : stop ) )
2015-07-08 00:33:24 +02:00
{
2018-04-30 18:39:06 +02:00
return - 1 ;
2015-07-15 20:11:32 +02:00
}
2017-02-17 20:35:57 +01:00
thread_ctrl : : wait_for ( 100 ) ;
2016-04-14 01:09:41 +02:00
}
2017-12-09 15:57:43 +01:00
2018-04-30 18:39:06 +02:00
return res ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdMachStat :
2014-08-22 23:15:02 +02:00
{
2015-07-16 13:32:19 +02:00
// HACK: "Not isolated" status
// Return SPU Interrupt status in LSB
2018-04-30 18:39:06 +02:00
return interrupts_enabled = = true ;
2014-08-22 23:15:02 +02:00
}
}
2014-10-02 12:29:20 +02:00
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Unknown/illegal channel (ch=%d [%s]) " HERE , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " ) ;
2014-08-22 23:15:02 +02:00
}
2016-04-19 15:04:02 +02:00
bool SPUThread : : set_ch_value ( u32 ch , u32 value )
2014-08-22 23:15:02 +02:00
{
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " set_ch_value(ch=%d [%s], value=0x%x) " , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " , value ) ;
2014-10-02 12:29:20 +02:00
2014-08-22 23:15:02 +02:00
switch ( ch )
{
2017-02-13 14:12:24 +01:00
case SPU_WrSRR0 :
{
srr0 = value ;
2018-01-15 14:18:13 +01:00
return true ;
2017-02-13 14:12:24 +01:00
}
2017-12-09 15:57:43 +01:00
2014-08-22 23:15:02 +02:00
case SPU_WrOutIntrMbox :
{
2016-04-14 01:09:41 +02:00
if ( offset > = RAW_SPU_BASE_ADDR )
2014-08-22 23:15:02 +02:00
{
2015-07-03 18:07:36 +02:00
while ( ! ch_out_intr_mbox . try_push ( value ) )
2014-08-22 23:15:02 +02:00
{
2016-08-09 16:14:41 +02:00
if ( test ( state & cpu_flag : : stop ) )
2016-04-14 01:09:41 +02:00
{
2016-04-19 15:04:02 +02:00
return false ;
2016-04-14 01:09:41 +02:00
}
2015-07-03 18:07:36 +02:00
2016-07-27 23:43:22 +02:00
thread_ctrl : : wait ( ) ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
2015-07-12 23:02:02 +02:00
int_ctrl [ 2 ] . set ( SPU_INT2_STAT_MAILBOX_INT ) ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2017-12-09 15:57:43 +01:00
2017-02-05 00:26:57 +01:00
const u32 code = value > > 24 ;
2014-08-22 23:15:02 +02:00
{
if ( code < 64 )
{
/* ===== sys_spu_thread_send_event (used by spu_printf) ===== */
2017-02-05 00:26:57 +01:00
u32 spup = code & 63 ;
u32 data ;
2014-08-22 23:15:02 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( data ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_send_event(value=0x%x, spup=%d): Out_MBox is empty " HERE , value , spup ) ;
2014-08-22 23:15:02 +02:00
}
2015-07-17 18:27:12 +02:00
if ( u32 count = ch_in_mbox . get_count ( ) )
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_send_event(value=0x%x, spup=%d): In_MBox is not empty (count=%d) " HERE , value , spup , count ) ;
2015-07-17 18:27:12 +02:00
}
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x) " , spup , value & 0x00ffffff , data ) ;
2014-08-22 23:15:02 +02:00
2017-02-05 00:26:57 +01:00
const auto queue = ( semaphore_lock { group - > mutex } , this - > spup [ spup ] . lock ( ) ) ;
2014-08-22 23:15:02 +02:00
2015-03-04 05:42:04 +01:00
if ( ! queue )
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_WARNING ( SPU , " sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected " , spup , ( value & 0x00ffffff ) , data ) ;
2017-02-24 14:19:11 +01:00
ch_in_mbox . set_values ( 1 , CELL_ENOTCONN ) ;
return true ;
2014-08-22 23:15:02 +02:00
}
2017-02-24 14:19:11 +01:00
ch_in_mbox . set_values ( 1 , CELL_OK ) ;
2017-02-03 22:36:04 +01:00
if ( ! queue - > send ( SYS_SPU_THREAD_EVENT_USER_KEY , id , ( ( u64 ) spup < < 32 ) | ( value & 0x00ffffff ) , data ) )
2014-08-22 23:15:02 +02:00
{
2017-02-24 14:19:11 +01:00
ch_in_mbox . set_values ( 1 , CELL_EBUSY ) ;
2014-08-22 23:15:02 +02:00
}
2017-02-24 14:19:11 +01:00
return true ;
2014-08-22 23:15:02 +02:00
}
else if ( code < 128 )
{
/* ===== sys_spu_thread_throw_event ===== */
2017-02-05 00:26:57 +01:00
u32 spup = code & 63 ;
u32 data ;
2015-07-17 18:27:12 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( data ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_throw_event(value=0x%x, spup=%d): Out_MBox is empty " HERE , value , spup ) ;
2014-08-22 23:15:02 +02:00
}
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x) " , spup , value & 0x00ffffff , data ) ;
2014-08-22 23:15:02 +02:00
2017-02-05 00:26:57 +01:00
const auto queue = ( semaphore_lock { group - > mutex } , this - > spup [ spup ] . lock ( ) ) ;
2014-08-22 23:15:02 +02:00
2015-03-04 05:42:04 +01:00
if ( ! queue )
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_WARNING ( SPU , " sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected " , spup , ( value & 0x00ffffff ) , data ) ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
// TODO: check passing spup value
2017-02-03 22:36:04 +01:00
if ( ! queue - > send ( SYS_SPU_THREAD_EVENT_USER_KEY , id , ( ( u64 ) spup < < 32 ) | ( value & 0x00ffffff ) , data ) )
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_WARNING ( SPU , " sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x) failed (queue is full) " , spup , ( value & 0x00ffffff ) , data ) ;
2014-08-22 23:15:02 +02:00
}
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
else if ( code = = 128 )
{
/* ===== sys_event_flag_set_bit ===== */
2017-02-05 00:26:57 +01:00
u32 flag = value & 0xffffff ;
u32 data ;
2015-07-17 18:27:12 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( data ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_event_flag_set_bit(value=0x%x (flag=%d)): Out_MBox is empty " HERE , value , flag ) ;
2014-08-22 23:15:02 +02:00
}
2015-07-17 18:27:12 +02:00
if ( u32 count = ch_in_mbox . get_count ( ) )
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_event_flag_set_bit(value=0x%x (flag=%d)): In_MBox is not empty (%d) " HERE , value , flag , count ) ;
2015-07-17 18:27:12 +02:00
}
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " sys_event_flag_set_bit(id=%d, value=0x%x (flag=%d)) " , data , value , flag ) ;
2014-08-22 23:15:02 +02:00
2017-02-24 14:19:11 +01:00
ch_in_mbox . set_values ( 1 , CELL_OK ) ;
2017-02-03 17:27:03 +01:00
// Use the syscall to set flag
2017-02-24 14:19:11 +01:00
if ( s32 res = sys_event_flag_set ( data , 1ull < < flag ) )
{
ch_in_mbox . set_values ( 1 , res ) ;
}
return true ;
2014-08-22 23:15:02 +02:00
}
else if ( code = = 192 )
{
/* ===== sys_event_flag_set_bit_impatient ===== */
2017-02-05 00:26:57 +01:00
u32 flag = value & 0xffffff ;
u32 data ;
2015-07-17 18:27:12 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( data ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_event_flag_set_bit_impatient(value=0x%x (flag=%d)): Out_MBox is empty " HERE , value , flag ) ;
2014-08-22 23:15:02 +02:00
}
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " sys_event_flag_set_bit_impatient(id=%d, value=0x%x (flag=%d)) " , data , value , flag ) ;
2014-08-22 23:15:02 +02:00
2017-02-03 17:27:03 +01:00
// Use the syscall to set flag
sys_event_flag_set ( data , 1ull < < flag ) ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
else
{
2015-03-02 03:10:41 +01:00
if ( ch_out_mbox . get_count ( ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " SPU_WrOutIntrMbox: unknown data (value=0x%x); Out_MBox = 0x%x " HERE , value , ch_out_mbox . get_value ( ) ) ;
2014-08-22 23:15:02 +02:00
}
else
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " SPU_WrOutIntrMbox: unknown data (value=0x%x) " HERE , value ) ;
2014-08-22 23:15:02 +02:00
}
}
}
}
case SPU_WrOutMbox :
{
2018-04-03 21:42:47 +02:00
while ( ! ch_out_mbox . try_push ( value ) )
{
2016-08-09 16:14:41 +02:00
if ( test ( state & cpu_flag : : stop ) )
2016-04-14 01:09:41 +02:00
{
2016-04-19 15:04:02 +02:00
return false ;
2016-04-14 01:09:41 +02:00
}
2015-07-03 18:07:36 +02:00
2016-07-27 23:43:22 +02:00
thread_ctrl : : wait ( ) ;
2014-12-23 00:31:11 +01:00
}
2015-03-02 03:10:41 +01:00
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_WrTagMask :
{
2015-03-02 03:10:41 +01:00
ch_tag_mask = value ;
2018-03-24 22:03:32 +01:00
if ( ch_tag_upd )
{
const u32 completed = get_mfc_completed ( ) ;
if ( completed & & ch_tag_upd = = 1 )
{
ch_tag_stat . set_value ( completed ) ;
ch_tag_upd = 0 ;
}
else if ( completed = = value & & ch_tag_upd = = 2 )
{
ch_tag_stat . set_value ( completed ) ;
ch_tag_upd = 0 ;
}
}
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_WrTagUpdate :
{
2017-02-17 20:35:57 +01:00
if ( value > 2 )
{
break ;
}
2018-03-24 22:03:32 +01:00
const u32 completed = get_mfc_completed ( ) ;
2017-02-17 20:35:57 +01:00
2018-03-24 22:03:32 +01:00
if ( ! value )
2017-05-15 20:54:12 +02:00
{
2018-04-21 22:11:08 +02:00
ch_tag_upd = 0 ;
2018-03-24 22:03:32 +01:00
ch_tag_stat . set_value ( completed ) ;
2017-05-15 20:54:12 +02:00
}
2018-03-24 22:03:32 +01:00
else if ( completed & & value = = 1 )
2017-02-17 20:35:57 +01:00
{
2018-04-21 22:11:08 +02:00
ch_tag_upd = 0 ;
2018-03-24 22:03:32 +01:00
ch_tag_stat . set_value ( completed ) ;
2017-02-17 20:35:57 +01:00
}
2018-03-24 22:03:32 +01:00
else if ( completed = = ch_tag_mask & & value = = 2 )
2017-02-17 20:35:57 +01:00
{
2018-04-21 22:11:08 +02:00
ch_tag_upd = 0 ;
2017-02-17 20:35:57 +01:00
ch_tag_stat . set_value ( completed ) ;
}
else
{
2018-03-24 22:03:32 +01:00
ch_tag_upd = value ;
ch_tag_stat . set_value ( 0 , false ) ;
2017-02-17 20:35:57 +01:00
}
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_LSA :
{
2017-02-17 20:35:57 +01:00
ch_mfc_cmd . lsa = value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_EAH :
{
2017-02-17 20:35:57 +01:00
ch_mfc_cmd . eah = value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_EAL :
{
2017-02-17 20:35:57 +01:00
ch_mfc_cmd . eal = value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_Size :
{
2017-11-29 13:28:41 +01:00
ch_mfc_cmd . size = value & 0x7fff ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case MFC_TagID :
2014-08-22 23:15:02 +02:00
{
2017-11-29 13:28:41 +01:00
ch_mfc_cmd . tag = value & 0x1f ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case MFC_Cmd :
2014-08-22 23:15:02 +02:00
{
2017-02-17 20:35:57 +01:00
ch_mfc_cmd . cmd = MFC ( value & 0xff ) ;
2018-03-24 22:03:32 +01:00
return process_mfc_cmd ( ch_mfc_cmd ) ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case MFC_WrListStallAck :
2014-08-22 23:15:02 +02:00
{
2017-02-17 20:35:57 +01:00
// Reset stall status for specified tag
2018-03-24 22:03:32 +01:00
if ( : : test_and_reset ( ch_stall_mask , 1u < < value ) )
2014-10-02 12:29:20 +02:00
{
2018-04-08 13:03:00 +02:00
do_mfc ( true ) ;
2014-10-02 12:29:20 +02:00
}
2015-03-02 03:10:41 +01:00
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case SPU_WrDec :
2014-08-22 23:15:02 +02:00
{
2015-07-06 01:21:15 +02:00
ch_dec_start_timestamp = get_timebased_time ( ) ;
2015-03-02 03:10:41 +01:00
ch_dec_value = value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case SPU_WrEventMask :
2014-08-22 23:15:02 +02:00
{
2015-07-16 13:32:19 +02:00
// detect masking events with enabled interrupt status
2017-12-01 03:50:01 +01:00
if ( value & ~ SPU_EVENT_INTR_IMPLEMENTED & & interrupts_enabled )
2015-07-16 13:32:19 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " SPU Interrupts not implemented (mask=0x%x) " HERE , value ) ;
2015-07-16 13:32:19 +02:00
}
// detect masking unimplemented events
2015-07-15 20:11:32 +02:00
if ( value & ~ SPU_EVENT_IMPLEMENTED )
2014-12-23 00:31:11 +01:00
{
2015-03-02 03:10:41 +01:00
break ;
2014-12-23 00:31:11 +01:00
}
2014-08-22 23:15:02 +02:00
2015-09-18 00:41:14 +02:00
ch_event_mask = value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case SPU_WrEventAck :
2014-08-22 23:15:02 +02:00
{
2015-07-15 20:11:32 +02:00
if ( value & ~ SPU_EVENT_IMPLEMENTED )
2015-07-21 22:14:04 +02:00
{
break ;
}
2015-03-02 03:10:41 +01:00
ch_event_stat & = ~ value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2017-03-11 19:55:50 +01:00
case 69 :
{
return true ;
}
2014-08-22 23:15:02 +02:00
}
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Unknown/illegal channel (ch=%d [%s], value=0x%x) " HERE , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " , value ) ;
2015-03-02 03:10:41 +01:00
}
2016-04-19 15:04:02 +02:00
bool SPUThread : : stop_and_signal ( u32 code )
2015-03-02 03:10:41 +01:00
{
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " stop_and_signal(code=0x%x) " , code ) ;
2014-08-22 23:15:02 +02:00
2016-04-14 01:09:41 +02:00
if ( offset > = RAW_SPU_BASE_ADDR )
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
status . atomic_op ( [ code ] ( u32 & status )
{
status = ( status & 0xffff ) | ( code < < 16 ) ;
status | = SPU_STATUS_STOPPED_BY_STOP ;
status & = ~ SPU_STATUS_RUNNING ;
} ) ;
2014-08-22 23:15:02 +02:00
2015-07-12 23:02:02 +02:00
int_ctrl [ 2 ] . set ( SPU_INT2_STAT_SPU_STOP_AND_SIGNAL_INT ) ;
2016-08-09 16:14:41 +02:00
state + = cpu_flag : : stop ;
2016-04-19 15:04:02 +02:00
return true ; // ???
2015-03-02 03:10:41 +01:00
}
2014-08-22 23:15:02 +02:00
switch ( code )
{
2017-02-13 16:18:05 +01:00
case 0x000 :
{
2017-02-24 14:28:05 +01:00
LOG_WARNING ( SPU , " STOP 0x0 " ) ;
// HACK: find an ILA instruction
for ( u32 addr = pc ; addr < 0x40000 ; addr + = 4 )
{
const u32 instr = _ref < u32 > ( addr ) ;
if ( instr > > 25 = = 0x21 )
{
pc = addr ;
return false ;
}
if ( instr > 0x1fffff )
{
break ;
}
}
// HACK: wait for executable code
while ( ! _ref < u32 > ( pc ) )
{
if ( test ( state & cpu_flag : : stop ) )
{
return false ;
}
thread_ctrl : : wait_for ( 1000 ) ;
}
return false ;
2017-02-13 16:18:05 +01:00
}
2014-10-02 12:29:20 +02:00
case 0x001 :
{
2017-02-05 15:06:03 +01:00
thread_ctrl : : wait_for ( 1000 ) ; // hack
2016-04-19 15:04:02 +02:00
return true ;
2014-10-02 12:29:20 +02:00
}
case 0x002 :
{
2016-08-09 16:14:41 +02:00
state + = cpu_flag : : ret ;
2016-04-19 15:04:02 +02:00
return true ;
2014-10-02 12:29:20 +02:00
}
2014-08-27 23:04:55 +02:00
case 0x110 :
2014-08-22 23:15:02 +02:00
{
2014-08-27 23:04:55 +02:00
/* ===== sys_spu_thread_receive_event ===== */
2017-02-05 00:26:57 +01:00
u32 spuq ;
2015-07-17 18:27:12 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( spuq ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_receive_event(): Out_MBox is empty " HERE ) ;
2014-08-22 23:15:02 +02:00
}
2015-07-17 18:27:12 +02:00
if ( u32 count = ch_in_mbox . get_count ( ) )
2014-08-22 23:15:02 +02:00
{
2015-07-17 18:27:12 +02:00
LOG_ERROR ( SPU , " sys_spu_thread_receive_event(): In_MBox is not empty (%d) " , count ) ;
2016-04-19 15:04:02 +02:00
return ch_in_mbox . set_values ( 1 , CELL_EBUSY ) , true ;
2014-08-22 23:15:02 +02:00
}
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " sys_spu_thread_receive_event(spuq=0x%x) " , spuq ) ;
2014-08-22 23:15:02 +02:00
2015-07-03 18:07:36 +02:00
if ( group - > type & SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT ) // this check may be inaccurate
{
2016-04-19 15:04:02 +02:00
return ch_in_mbox . set_values ( 1 , CELL_EINVAL ) , true ;
2015-07-03 18:07:36 +02:00
}
2017-01-29 17:50:18 +01:00
std : : shared_ptr < lv2_event_queue > queue ;
2015-03-05 14:18:06 +01:00
2017-02-05 00:26:57 +01:00
while ( true )
2015-03-05 14:18:06 +01:00
{
2017-02-05 00:26:57 +01:00
queue . reset ( ) ;
2015-03-05 14:18:06 +01:00
2017-02-05 00:26:57 +01:00
// Check group status, wait if necessary
while ( group - > run_state > = SPU_THREAD_GROUP_STATUS_WAITING & & group - > run_state < = SPU_THREAD_GROUP_STATUS_SUSPENDED )
{
if ( test ( state & cpu_flag : : stop ) )
2015-03-05 14:18:06 +01:00
{
2017-02-05 00:26:57 +01:00
return false ;
2015-03-05 14:18:06 +01:00
}
2017-02-05 00:26:57 +01:00
thread_ctrl : : wait ( ) ;
2015-03-05 14:18:06 +01:00
}
2017-02-05 00:26:57 +01:00
reader_lock rlock ( id_manager : : g_mutex ) ;
2014-12-23 00:31:11 +01:00
2017-02-05 00:26:57 +01:00
semaphore_lock lock ( group - > mutex ) ;
2015-07-03 18:07:36 +02:00
2017-02-05 00:26:57 +01:00
if ( group - > run_state > = SPU_THREAD_GROUP_STATUS_WAITING & & group - > run_state < = SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED )
2016-04-14 01:09:41 +02:00
{
2017-02-05 00:26:57 +01:00
// Try again
continue ;
2016-04-14 01:09:41 +02:00
}
2015-07-03 18:07:36 +02:00
2017-02-05 00:26:57 +01:00
for ( auto & v : this - > spuq )
2014-12-23 00:31:11 +01:00
{
2017-02-05 00:26:57 +01:00
if ( spuq = = v . first )
2016-04-14 01:09:41 +02:00
{
2017-02-05 00:26:57 +01:00
queue = v . second . lock ( ) ;
if ( queue )
{
break ;
}
2016-04-14 01:09:41 +02:00
}
2014-12-23 00:31:11 +01:00
}
2015-07-03 18:07:36 +02:00
2017-02-05 00:26:57 +01:00
if ( ! queue )
{
return ch_in_mbox . set_values ( 1 , CELL_EINVAL ) , true ; // TODO: check error value
}
semaphore_lock qlock ( queue - > mutex ) ;
2015-07-19 14:58:11 +02:00
2017-02-03 22:36:04 +01:00
if ( queue - > events . empty ( ) )
2015-07-19 14:58:11 +02:00
{
2017-02-03 22:36:04 +01:00
queue - > sq . emplace_back ( this ) ;
2017-02-05 00:26:57 +01:00
group - > run_state = SPU_THREAD_GROUP_STATUS_WAITING ;
for ( auto & thread : group - > threads )
{
if ( thread )
{
thread - > state + = cpu_flag : : suspend ;
}
}
// Wait
break ;
2017-02-03 22:36:04 +01:00
}
else
{
2017-02-05 00:26:57 +01:00
// Return the event immediately
2017-02-03 22:36:04 +01:00
const auto event = queue - > events . front ( ) ;
const auto data1 = static_cast < u32 > ( std : : get < 1 > ( event ) ) ;
const auto data2 = static_cast < u32 > ( std : : get < 2 > ( event ) ) ;
const auto data3 = static_cast < u32 > ( std : : get < 3 > ( event ) ) ;
ch_in_mbox . set_values ( 4 , CELL_OK , data1 , data2 , data3 ) ;
queue - > events . pop_front ( ) ;
2017-02-05 00:26:57 +01:00
return true ;
2017-02-03 22:36:04 +01:00
}
}
2015-07-01 00:25:52 +02:00
2017-02-05 00:26:57 +01:00
while ( true )
2017-02-03 22:36:04 +01:00
{
if ( test ( state & cpu_flag : : stop ) )
{
return false ;
2015-07-19 14:58:11 +02:00
}
2015-03-04 05:42:04 +01:00
2017-02-05 00:26:57 +01:00
if ( ! state . test_and_reset ( cpu_flag : : signal ) )
{
thread_ctrl : : wait ( ) ;
}
else
{
break ;
}
2015-07-03 18:07:36 +02:00
}
2017-02-05 00:26:57 +01:00
semaphore_lock lock ( group - > mutex ) ;
2017-12-09 15:57:43 +01:00
2017-02-05 00:26:57 +01:00
if ( group - > run_state = = SPU_THREAD_GROUP_STATUS_WAITING )
2015-07-03 18:07:36 +02:00
{
2017-02-05 00:26:57 +01:00
group - > run_state = SPU_THREAD_GROUP_STATUS_RUNNING ;
2015-07-03 18:07:36 +02:00
}
2017-02-05 00:26:57 +01:00
else if ( group - > run_state = = SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED )
2015-07-03 18:07:36 +02:00
{
2017-02-05 00:26:57 +01:00
group - > run_state = SPU_THREAD_GROUP_STATUS_SUSPENDED ;
2015-07-03 18:07:36 +02:00
}
2015-03-05 22:29:05 +01:00
2015-07-19 14:58:11 +02:00
for ( auto & thread : group - > threads )
2015-03-05 22:29:05 +01:00
{
2017-02-05 00:26:57 +01:00
if ( thread )
2016-04-14 01:09:41 +02:00
{
2016-08-09 16:14:41 +02:00
thread - > state - = cpu_flag : : suspend ;
2017-02-05 00:26:57 +01:00
if ( thread . get ( ) ! = this )
{
thread - > notify ( ) ;
}
2016-04-14 01:09:41 +02:00
}
2015-03-05 22:29:05 +01:00
}
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2014-08-27 23:04:55 +02:00
2018-02-04 12:47:49 +01:00
case 0x100 :
{
if ( ch_out_mbox . get_count ( ) )
{
fmt : : throw_exception ( " STOP code 0x100: Out_MBox is not empty " HERE ) ;
}
_mm_mfence ( ) ;
return true ;
}
2014-08-27 23:04:55 +02:00
case 0x101 :
{
/* ===== sys_spu_thread_group_exit ===== */
2017-02-05 00:26:57 +01:00
u32 value ;
2015-07-17 18:27:12 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( value ) )
2014-08-27 23:04:55 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_group_exit(): Out_MBox is empty " HERE ) ;
2014-08-27 23:04:55 +02:00
}
2015-03-02 03:10:41 +01:00
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " sys_spu_thread_group_exit(status=0x%x) " , value ) ;
2015-03-02 03:10:41 +01:00
2017-02-05 00:26:57 +01:00
semaphore_lock lock ( group - > mutex ) ;
2015-03-02 03:10:41 +01:00
2015-09-04 01:23:31 +02:00
for ( auto & thread : group - > threads )
2014-08-27 23:04:55 +02:00
{
2015-07-19 14:58:11 +02:00
if ( thread & & thread . get ( ) ! = this )
2014-08-27 23:04:55 +02:00
{
2016-08-09 16:14:41 +02:00
thread - > state + = cpu_flag : : stop ;
2016-09-07 00:38:52 +02:00
thread - > notify ( ) ;
2014-08-27 23:04:55 +02:00
}
}
2017-02-05 00:26:57 +01:00
group - > run_state = SPU_THREAD_GROUP_STATUS_INITIALIZED ;
2015-03-04 22:51:14 +01:00
group - > exit_status = value ;
2015-03-05 14:18:06 +01:00
group - > join_state | = SPU_TGJSF_GROUP_EXIT ;
2015-07-03 18:07:36 +02:00
group - > cv . notify_one ( ) ;
2015-07-01 00:25:52 +02:00
2016-08-09 16:14:41 +02:00
state + = cpu_flag : : stop ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-27 23:04:55 +02:00
}
2014-08-22 23:15:02 +02:00
case 0x102 :
2014-08-27 23:04:55 +02:00
{
/* ===== sys_spu_thread_exit ===== */
2015-03-02 03:10:41 +01:00
if ( ! ch_out_mbox . get_count ( ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_exit(): Out_MBox is empty " HERE ) ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " sys_spu_thread_exit(status=0x%x) " , ch_out_mbox . get_value ( ) ) ;
2015-03-02 03:10:41 +01:00
2017-02-05 00:26:57 +01:00
semaphore_lock lock ( group - > mutex ) ;
2015-07-03 18:07:36 +02:00
2015-03-02 03:10:41 +01:00
status | = SPU_STATUS_STOPPED_BY_STOP ;
2015-07-03 18:07:36 +02:00
group - > cv . notify_one ( ) ;
2015-07-01 00:25:52 +02:00
2016-08-09 16:14:41 +02:00
state + = cpu_flag : : stop ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-27 23:04:55 +02:00
}
2015-03-02 03:10:41 +01:00
}
2014-08-27 23:04:55 +02:00
2015-03-02 03:10:41 +01:00
if ( ! ch_out_mbox . get_count ( ) )
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Unknown STOP code: 0x%x (Out_MBox is empty) " HERE , code ) ;
2015-03-02 03:10:41 +01:00
}
else
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Unknown STOP code: 0x%x (Out_MBox=0x%x) " HERE , code , ch_out_mbox . get_value ( ) ) ;
2015-03-02 03:10:41 +01:00
}
}
void SPUThread : : halt ( )
{
2016-01-12 22:57:16 +01:00
LOG_TRACE ( SPU , " halt() " ) ;
2015-03-02 03:10:41 +01:00
2016-04-14 01:09:41 +02:00
if ( offset > = RAW_SPU_BASE_ADDR )
2015-03-02 03:10:41 +01:00
{
status . atomic_op ( [ ] ( u32 & status )
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
status | = SPU_STATUS_STOPPED_BY_HALT ;
status & = ~ SPU_STATUS_RUNNING ;
} ) ;
2015-07-12 23:02:02 +02:00
int_ctrl [ 2 ] . set ( SPU_INT2_STAT_SPU_HALT_OR_STEP_INT ) ;
2015-07-01 00:25:52 +02:00
2016-08-09 16:14:41 +02:00
throw cpu_flag : : stop ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
status | = SPU_STATUS_STOPPED_BY_HALT ;
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Halt " HERE ) ;
2014-10-24 15:24:09 +02:00
}
2014-11-19 15:16:30 +01:00
2016-04-14 01:09:41 +02:00
void SPUThread : : fast_call ( u32 ls_addr )
2014-11-19 15:16:30 +01:00
{
2016-04-14 01:09:41 +02:00
// LS:0x0: this is originally the entry point of the interrupt handler, but interrupts are not implemented
_ref < u32 > ( 0 ) = 0x00000002 ; // STOP 2
2015-07-01 00:25:52 +02:00
2016-04-14 01:09:41 +02:00
auto old_pc = pc ;
auto old_lr = gpr [ 0 ] . _u32 [ 3 ] ;
auto old_stack = gpr [ 1 ] . _u32 [ 3 ] ; // only saved and restored (may be wrong)
pc = ls_addr ;
gpr [ 0 ] . _u32 [ 3 ] = 0x0 ;
try
{
cpu_task ( ) ;
}
2016-08-09 16:14:41 +02:00
catch ( cpu_flag _s )
2016-04-14 01:09:41 +02:00
{
state + = _s ;
2016-08-09 16:14:41 +02:00
if ( _s ! = cpu_flag : : ret ) throw ;
2016-04-14 01:09:41 +02:00
}
2014-11-19 15:16:30 +01:00
2016-08-09 16:14:41 +02:00
state - = cpu_flag : : ret ;
2016-04-14 01:09:41 +02:00
pc = old_pc ;
gpr [ 0 ] . _u32 [ 3 ] = old_lr ;
gpr [ 1 ] . _u32 [ 3 ] = old_stack ;
2016-04-25 12:49:12 +02:00
}