forked from dangiu/PicoMemcard
-
Notifications
You must be signed in to change notification settings - Fork 0
/
psxSPI.pio
178 lines (164 loc) · 6.66 KB
/
psxSPI.pio
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
; Author: Daniele Giuliani
; Interfaces with the modified SPI protocol used by
; PSX to communicate with controller/memory cards
.program sel_monitor
; Input pins mapping:
; 0 - SEL
; Program description:
; Monitors SEL line, singnals other SM to start execution when SEL goes low
; generates interrupt signaling CPU to reset SMs when SEL goes high
wait 1 pin 0 ; wait for initial sync (SEL up)
.wrap_target
wait 0 pin 0 ; wait for SEL low
irq clear 4 ; signal other SMs SEL is low
wait 1 pin 0 ; wait for SEL to go HIGH
irq set 0 ; signal CPU that SEL went high
.wrap
.program cmd_reader
; Input pins mapping:
; 0 - CMD
; 1 - SEL (not used)
; 2 - CLK
; Program description:
; Samples CMD line during rising clock edges,
; waits for sel_monitor signal before starting execution
sel_high:
irq wait 4 ; wait for SEL monitor to signal SM start
.wrap_target
wait 0 pin 2 ; wait for clock to fall
wait 1 pin 2 ; wait for rising clock edge
in pins 1 ; sample 1 bit form CMD line
irq clear 1 ; signal ACK timer to start
.wrap
.program dat_reader
; Input pins mapping:
; 0 - DAT
; 1 - CMD (not used)
; 2 - SEL (not used)
; 3 - CLK
; Program description:
; Samples DAT line during rising clock edges,
; waits for sel_monitor signal before starting execution.
; Can be used for sniffing DAT line used by other hardware
sel_high:
irq wait 4 ; wait for SEL monitor to signal SM start
.wrap_target
wait 0 pin 3 ; wait for clock to fall
wait 1 pin 3 ; wait for rising clock edge
in pins 1 ; sample 1 bit form DAT line
.wrap
.program dat_writer
; Set pins mapping:
; 0 - DAT
; Output pins mapping:
; 0 - DAT
; Input pins mapping:
; 0 - CLK
; Program description:
; Outputs bits to the DAT line on falling clock edges.
; waits for sel_monitor signal before starting execution.
; Bits are outputted by changing pin direction:
; 0 -> set pin as input (Hi-Z) -> output a one
; 1 -> set pin as output low -> output a zero
set pindirs, 0 ; release DAT line (set pin as input = Hi-Z)
irq wait 4 ; wait for SEL monitor to signal SM start
pull ; manual pull in order to stall SM if TX fifo is empty
.wrap_target
wait 1 pin 0 ; check clock is high
wait 0 pin 0 ; wait for falling clock edge
out pindirs 1 ; output 1 bit (by changing pin direction)
.wrap
.program ack_sender
; Set pins mapping:
; 0 - ACK
; Program description:
; Sets ACK line low, waits for irq 1 to be lowered by cmd_reader.
; Timing is based on the first bit read by cmd_reader.
.wrap_target
irq wait 1 [31] ; 32 cycles delay (1 for instruction)
nop [23] ; 24 cycles delay (1 for instruction), total of 56 cycles
set pindirs, 1 [2] ; keep ack low for more than half PSX clock
set pindirs, 0
.wrap
% c-sdk {
#define ACK_CLKDIV 86 // with pico default clock (125 Mhz) results in a frequency around 6 time greater than PSX SPI frequency
#define ACK_IRQ 1
static inline void sel_monitor_program_init(PIO pio, uint sm, uint offset, uint pin_sel) {
pio_sm_config c = sel_monitor_program_get_default_config(offset);
/* Pin Configuration */
sm_config_set_in_pins(&c, pin_sel);
pio_sm_set_consecutive_pindirs(pio, sm, pin_sel, 1, false);
pio_gpio_init(pio, pin_sel); // SEL pin
/* Interrupt Configuration */
pio_set_irq0_source_enabled(pio, pis_interrupt0, true);
/* Initialize SM */
pio_sm_init(pio, sm, offset, &c);
}
static inline void cmd_reader_program_init(PIO pio, uint sm, uint offset, uint pin_cmd) {
pio_sm_config c = cmd_reader_program_get_default_config(offset);
/* Pin Configuration */
sm_config_set_in_pins(&c, pin_cmd);
pio_sm_set_consecutive_pindirs(pio, sm, pin_cmd, 3, false);
pio_gpio_init(pio, pin_cmd); // CMD pin
pio_gpio_init(pio, pin_cmd + 1); // SEL pin
pio_gpio_init(pio, pin_cmd + 2); // CLK pin
/* Fifo Configuration */
sm_config_set_in_shift(&c, true, true, 8); // shift ISR to right, autopush every 8 bits
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); // join RX FIFO
/* Initialize SM */
pio_sm_init(pio, sm, offset, &c);
}
static inline void dat_reader_program_init(PIO pio, uint sm, uint offset, uint pin_dat) {
pio_sm_config c = dat_reader_program_get_default_config(offset);
/* Pin Configuration */
sm_config_set_in_pins(&c, pin_dat);
pio_sm_set_consecutive_pindirs(pio, sm, pin_dat, 4, false);
pio_gpio_init(pio, pin_dat); // DAT pin
pio_gpio_init(pio, pin_dat + 1); // CMD pin
pio_gpio_init(pio, pin_dat + 2); // SEL pin
pio_gpio_init(pio, pin_dat + 3); // CLK pin
/* Fifo Configuration */
sm_config_set_in_shift(&c, true, true, 8); // shift ISR to right, autopush every 8 bits
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); // join RX FIFO
/* Initialize SM */
pio_sm_init(pio, sm, offset, &c);
}
static inline uint8_t read_byte_blocking(PIO pio, uint sm_cmd_reader) {
return (uint8_t) (pio_sm_get_blocking(pio, sm_cmd_reader) >> 24);
}
static inline void dat_writer_program_init(PIO pio, uint sm, uint offset, uint pin_dat, uint pin_clk) {
pio_sm_config c = dat_writer_program_get_default_config(offset);
/* Pin Configuration */
sm_config_set_in_pins(&c, pin_clk); // set base IN pin (CLK)
sm_config_set_out_pins(&c, pin_dat, 1); // set base OUT pin (DAT)
sm_config_set_set_pins(&c, pin_dat, 1); // set base SET pin (DAT)
pio_sm_set_pins_with_mask(pio, sm, 0x00000000, 1 << pin_dat); // set DAT pin to low output
pio_sm_set_consecutive_pindirs(pio, sm, pin_dat, 1, false); // set DAT pin as input (initial configuration)
pio_sm_set_consecutive_pindirs(pio, sm, pin_clk, 1, false); // set CLK pin as input
pio_gpio_init(pio, pin_dat); // init DAT pin
pio_gpio_init(pio, pin_clk); // init CLK pin
/* FIFO Configuration */
sm_config_set_out_shift(&c, true, true, 8); // shift OSR to right, autopull every 8 bits
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); // join TX FIFO
/* Initialize SM */
pio_sm_init(pio, sm, offset, &c);
}
static inline void write_byte_blocking(PIO pio, uint sm, uint32_t byte) {
byte = ~byte & (0x000000ff); // invert single bit values and mask off
// 0s must become ones in order to set pins to output a low signal
// 1s must be zeros to set pin to input creating a high-z state
pio_sm_put_blocking(pio, sm, byte); // place byte in tx fifo
}
static inline void ack_sender_program_init(PIO pio, uint sm, uint offset, uint pin_ack) {
pio_sm_config c = ack_sender_program_get_default_config(offset);
/* Pin Configuration */
sm_config_set_set_pins(&c, pin_ack, 1); // set base SET pin (ACK)
pio_sm_set_pins_with_mask(pio, sm, 0x00000000, 1 << pin_ack); // set ACK pin to low output
pio_sm_set_consecutive_pindirs(pio, sm, pin_ack, 1, false); // set ACK pin as input (initial configuration)
pio_gpio_init(pio, pin_ack); // init ACK pin
/* Clock Configuration */
sm_config_set_clkdiv_int_frac(&c, ACK_CLKDIV, 0x00);
/* Initialize SM */
pio_sm_init(pio, sm, offset, &c);
}
%}