generated from pimoroni/pico-boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 4
/
sram.c
173 lines (145 loc) · 7.3 KB
/
sram.c
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
// Copyright 2023 (c) Michael Bell
// The BSD 3 clause license applies
#include <hardware/pio.h>
#include <hardware/dma.h>
#include <pico/multicore.h>
#include "hardware/structs/bus_ctrl.h"
#include "sram.pio.h"
#include "sram.h"
// We define the SMs and DMA channels to avoid memory accesses
// looking them up which saves precious cycles processing the SPI commands.
#define pio_write_offset 0 // This must be 0
static int pio_read_offset;
uint8_t __attribute__((section(".spi_ram.emu_ram"))) emu_ram[65536];
static void setup_sram_pio()
{
pio_read_offset = pio_add_program(SIM_SRAM_pio_read, &sram_read_program);
pio_sm_claim(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm);
pio_add_program_at_offset(SIM_SRAM_pio_write, &sram_write_program, pio_write_offset);
pio_sm_claim(SIM_SRAM_pio_write, SIM_SRAM_pio_write_sm);
sram_read_program_init(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm, pio_read_offset, SIM_SRAM_SPI_MOSI);
sram_write_program_init(SIM_SRAM_pio_write, SIM_SRAM_pio_write_sm, pio_write_offset, SIM_SRAM_SPI_MOSI, SIM_SRAM_SPI_MISO);
}
static void setup_rx_channel()
{
dma_channel_claim(SIM_SRAM_rx_channel);
dma_channel_config c = dma_channel_get_default_config(SIM_SRAM_rx_channel);
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
channel_config_set_read_increment(&c, false);
channel_config_set_write_increment(&c, true);
channel_config_set_dreq(&c, pio_get_dreq(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm, false));
dma_channel_configure(
SIM_SRAM_rx_channel, // Channel to be configured
&c, // The configuration we just created
NULL, // The initial write address
&SIM_SRAM_pio_read->rxf[SIM_SRAM_pio_read_sm], // The initial read address
65536, // Number of transfers; in this case each is 1 byte.
false // Start immediately.
);
}
static void setup_tx_channel()
{
dma_channel_claim(SIM_SRAM_tx_channel);
dma_channel_claim(SIM_SRAM_tx_channel2);
dma_channel_config c = dma_channel_get_default_config(SIM_SRAM_tx_channel);
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
channel_config_set_read_increment(&c, true);
channel_config_set_write_increment(&c, false);
channel_config_set_bswap(&c, true);
channel_config_set_dreq(&c, pio_get_dreq(SIM_SRAM_pio_write, SIM_SRAM_pio_write_sm, true));
dma_channel_configure(
SIM_SRAM_tx_channel, // Channel to be configured
&c, // The configuration we just created
&SIM_SRAM_pio_write->txf[SIM_SRAM_pio_write_sm], // The initial write address
NULL, // The initial read address
65536, // Number of transfers; in this case each is 1 byte.
false // Start immediately.
);
c = dma_channel_get_default_config(SIM_SRAM_tx_channel2);
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
channel_config_set_read_increment(&c, false);
channel_config_set_write_increment(&c, false);
channel_config_set_dreq(&c, pio_get_dreq(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm, false));
dma_channel_configure(
SIM_SRAM_tx_channel2, // Channel to be configured
&c, // The configuration we just created
&dma_hw->ch[SIM_SRAM_tx_channel].al3_read_addr_trig, // The initial write address
&SIM_SRAM_pio_read->rxf[SIM_SRAM_pio_read_sm], // The initial read address
1, // Number of transfers; in this case each is 1 byte.
false // Start immediately.
);
}
static __always_inline void wait_for_cs_high() {
while (true) {
if (gpio_get(SIM_SRAM_SPI_CS)) {
if (gpio_get(SIM_SRAM_SPI_CS)) {
// Must be high for 2 cycles to count - avoids deselecting on a glitch.
break;
}
}
}
}
static void __scratch_x("core1_main") core1_main()
{
while (true) {
uint32_t cmd = pio_sm_get_blocking(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm);
if (cmd == 0x3) {
// Read - this works by transferring the address direct from the Read PIO SM
// direct to the read address of the transmit DMA channel.
dma_channel_start(SIM_SRAM_tx_channel2);
wait_for_cs_high();
dma_channel_abort(SIM_SRAM_tx_channel);
}
else if (cmd == 0xB) {
// Fast read
// Need to patch the write program to do extra delay cycles
SIM_SRAM_pio_write->instr_mem[sram_write_offset_addr_loop_end] = pio_encode_jmp(sram_write_offset_fast_read);
// And change the write size to 8
hw_clear_bits(&dma_hw->ch[SIM_SRAM_tx_channel].al1_ctrl, DMA_CH0_CTRL_TRIG_DATA_SIZE_BITS);
hw_set_bits(&SIM_SRAM_pio_write->sm[SIM_SRAM_pio_write_sm].shiftctrl, 8 << PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB);
// Transfer the address manually
uint32_t addr = pio_sm_get_blocking(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm);
addr |= pio_sm_get_blocking(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm);
dma_hw->ch[SIM_SRAM_tx_channel].al3_read_addr_trig = addr;
wait_for_cs_high();
dma_channel_abort(SIM_SRAM_tx_channel);
// Unpatch the write program
SIM_SRAM_pio_write->instr_mem[sram_write_offset_addr_loop_end] = pio_encode_jmp_pin(sram_write_offset_addr_two);
// And change the write size back to 32
hw_set_bits(&dma_hw->ch[SIM_SRAM_tx_channel].al1_ctrl, 2 << DMA_CH10_CTRL_TRIG_DATA_SIZE_LSB);
hw_clear_bits(&SIM_SRAM_pio_write->sm[SIM_SRAM_pio_write_sm].shiftctrl, PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS);
}
else if (cmd == 0x2) {
// Write
//addr += pio_sm_get_blocking(pio, pio_read_sm) << 8;
uint32_t addr = pio_sm_get_blocking(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm);
addr |= pio_sm_get_blocking(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm);
dma_hw->ch[SIM_SRAM_rx_channel].al2_write_addr_trig = addr;
wait_for_cs_high();
while (!pio_sm_is_rx_fifo_empty(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm));
dma_channel_abort(SIM_SRAM_rx_channel);
}
else {
// Ignore unknown command
wait_for_cs_high();
}
pio_sm_set_enabled(SIM_SRAM_pio_write, SIM_SRAM_pio_write_sm, false);
pio_sm_clear_fifos(SIM_SRAM_pio_write, SIM_SRAM_pio_write_sm);
pio_sm_restart(SIM_SRAM_pio_write, SIM_SRAM_pio_write_sm);
pio_sm_exec(SIM_SRAM_pio_write, SIM_SRAM_pio_write_sm, pio_encode_jmp(pio_write_offset));
pio_sm_set_enabled(SIM_SRAM_pio_write, SIM_SRAM_pio_write_sm, true);
pio_sm_set_enabled(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm, false);
pio_sm_clear_fifos(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm);
pio_sm_restart(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm);
pio_sm_exec(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm, pio_encode_jmp(pio_read_offset));
pio_sm_set_enabled(SIM_SRAM_pio_read, SIM_SRAM_pio_read_sm, true);
}
}
uint8_t* setup_simulated_sram() {
setup_sram_pio();
setup_rx_channel();
setup_tx_channel();
hw_set_bits(&bus_ctrl_hw->priority, BUSCTRL_BUS_PRIORITY_DMA_R_BITS | BUSCTRL_BUS_PRIORITY_DMA_W_BITS);
multicore_launch_core1(core1_main);
return emu_ram;
}