Skip to content

Commit fa5c31a

Browse files
authored
Merge pull request #204 from tessel/stability
Stability fixes
2 parents 1d3e139 + 45250d8 commit fa5c31a

File tree

12 files changed

+201
-69
lines changed

12 files changed

+201
-69
lines changed

Diff for: common/dma.c

+22-9
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,24 @@ void dma_init() {
2121
}
2222

2323
void dma_abort(DmaChan chan) {
24+
__disable_irq();
2425
DMAC->CHID.reg = chan;
2526
DMAC->CHCTRLA.reg = 0;
27+
__enable_irq();
28+
}
29+
30+
void dma_start(DmaChan chan) {
31+
__disable_irq();
32+
DMAC->CHID.reg = chan;
33+
DMAC->CHCTRLA.reg = DMAC_CHCTRLA_ENABLE;
34+
__enable_irq();
35+
}
36+
37+
void dma_enable_interrupt(DmaChan chan) {
38+
__disable_irq();
39+
DMAC->CHID.reg = chan;
40+
DMAC->CHINTENSET.reg = DMAC_CHINTENSET_TCMPL | DMAC_CHINTENSET_TERR;
41+
__enable_irq();
2642
}
2743

2844
u32 dma_remaining(DmaChan chan) {
@@ -78,24 +94,21 @@ void dma_link_chain(DmacDescriptor* chain, u32 count) {
7894
}
7995

8096
void dma_start_descriptor(DmaChan chan, DmacDescriptor* chain) {
81-
DMAC->CHID.reg = chan;
82-
DMAC->CHCTRLA.reg = 0;
97+
dma_abort(chan);
8398
memcpy(&dma_descriptors[chan], &chain[0], sizeof(DmacDescriptor));
84-
DMAC->CHCTRLA.reg = DMAC_CHCTRLA_ENABLE;
99+
dma_start(chan);
85100
}
86101

87102
void dma_sercom_start_tx(DmaChan chan, SercomId id, u8* src, unsigned size) {
88-
DMAC->CHID.reg = chan;
89-
DMAC->CHCTRLA.reg = 0;
103+
dma_abort(chan);
90104
dma_fill_sercom_tx(&dma_descriptors[chan], id, src, size);
91105
dma_descriptors[chan].DESCADDR.reg = 0;
92-
DMAC->CHCTRLA.reg = DMAC_CHCTRLA_ENABLE;
106+
dma_start(chan);
93107
}
94108

95109
void dma_sercom_start_rx(DmaChan chan, SercomId id, u8* dst, unsigned size) {
96-
DMAC->CHID.reg = chan;
97-
DMAC->CHCTRLA.reg = 0;
110+
dma_abort(chan);
98111
dma_fill_sercom_rx(&dma_descriptors[chan], id, dst, size);
99112
dma_descriptors[chan].DESCADDR.reg = 0;
100-
DMAC->CHCTRLA.reg = DMAC_CHCTRLA_ENABLE;
113+
dma_start(chan);
101114
}

Diff for: common/hw.h

+1
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ void dma_init();
187187
void dma_sercom_start_tx(DmaChan chan, SercomId id, u8* src, unsigned size);
188188
void dma_sercom_start_rx(DmaChan chan, SercomId id, u8* dst, unsigned size);
189189
void dma_abort(DmaChan chan);
190+
void dma_enable_interrupt(DmaChan chan);
190191
void dma_fill_sercom_tx(DmacDescriptor* desc, SercomId id, u8 *src, unsigned size);
191192
void dma_fill_sercom_rx(DmacDescriptor* desc, SercomId id, u8 *dst, unsigned size);
192193
void dma_sercom_configure_tx(DmaChan chan, SercomId id);

Diff for: common/samd21g18a_firmware_partition.ld

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ SEARCH_DIR(.)
5050
MEMORY
5151
{
5252
rom (rx) : ORIGIN = 0x00001000, LENGTH = 0x0003efff
53-
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00001000
53+
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
5454
}
5555

5656
/* The stack size used by the application. NOTE: you need to adjust according to your application. */

Diff for: firmware/bridge.c

+26-9
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,15 @@ void bridge_handle_sync() {
120120
for (u8 chan=0; chan<BRIDGE_NUM_CHAN; chan++) {
121121
u8 size = ctrl_rx.size[chan];
122122
if (ctrl_tx.status & (1<<chan) && size > 0) {
123+
out_chan_ready &= ~ (1<<chan);
123124
dma_fill_sercom_tx(&dma_chain_data_tx[desc], SERCOM_BRIDGE, NULL, size);
124125
dma_fill_sercom_rx(&dma_chain_data_rx[desc], SERCOM_BRIDGE, out_chan_ptr[chan], size);
125126
desc++;
126127
}
127128

128129
size = ctrl_tx.size[chan];
129130
if (ctrl_rx.status & (1<<chan) && size > 0) {
131+
in_chan_size[chan] = 0;
130132
dma_fill_sercom_tx(&dma_chain_data_tx[desc], SERCOM_BRIDGE, in_chan_ptr[chan], size);
131133
dma_fill_sercom_rx(&dma_chain_data_rx[desc], SERCOM_BRIDGE, NULL, size);
132134
desc++;
@@ -161,26 +163,33 @@ void bridge_handle_sync() {
161163
void bridge_dma_rx_completion() {
162164
if (bridge_state == BRIDGE_STATE_DATA) {
163165

166+
// Copy the global state to this stack frame in case SYNC changes and the ISR overwrites these
167+
uint8_t rx_status = ctrl_rx.status;
168+
uint8_t tx_status = ctrl_tx.status;
169+
uint8_t rx_size[BRIDGE_NUM_CHAN];
170+
memcpy(rx_size, ctrl_rx.size, sizeof(rx_size));
171+
uint8_t tx_size[BRIDGE_NUM_CHAN];
172+
memcpy(tx_size, ctrl_tx.size, sizeof(tx_size));
173+
__asm__ __volatile__ ("" : : : "memory");
174+
164175
#define CHECK_OPEN(x) \
165-
if ((ctrl_rx.status & (0x10<<x)) && !(was_open & (0x10<<x))) { \
166-
bridge_open_##x(ctrl_rx.size[x]); \
176+
if ((rx_status & (0x10<<x)) && !(was_open & (0x10<<x))) { \
177+
bridge_open_##x(rx_size[x]); \
167178
}
168179

169180
#define CHECK_COMPLETION_OUT(x) \
170-
if (ctrl_tx.status & (1<<x) && ctrl_rx.size[x] > 0) { \
171-
out_chan_ready &= ~ (1<<x); \
172-
bridge_completion_out_##x(ctrl_rx.size[x]); \
181+
if (tx_status & (1<<x) && rx_size[x] > 0) { \
182+
bridge_completion_out_##x(rx_size[x]); \
173183
}
174184

175185
#define CHECK_COMPLETION_IN(x) \
176-
if (ctrl_rx.status & (1<<x) && ctrl_tx.size[x] > 0) { \
177-
in_chan_size[x] = 0; \
186+
if (rx_status & (1<<x) && tx_size[x] > 0) { \
178187
bridge_completion_in_##x(); \
179188
}
180189

181190
#define CHECK_CLOSE(x) \
182-
if (!(ctrl_rx.status & (0x10<<x)) && (was_open & (0x10<<x))) { \
183-
bridge_close_##x(ctrl_rx.size[x]); \
191+
if (!(rx_status & (0x10<<x)) && (was_open & (0x10<<x))) { \
192+
bridge_close_##x(rx_size[x]); \
184193
}
185194

186195
CHECK_OPEN(0)
@@ -211,24 +220,32 @@ void bridge_dma_rx_completion() {
211220
}
212221

213222
void bridge_start_in(u8 channel, u8* data, u8 length) {
223+
__disable_irq();
214224
in_chan_ptr[channel] = data;
215225
in_chan_size[channel] = length;
226+
__enable_irq();
216227
pin_high(PIN_BRIDGE_IRQ);
217228
}
218229

219230
void bridge_start_out(u8 channel, u8* data) {
231+
__disable_irq();
220232
out_chan_ptr[channel] = data;
221233
out_chan_ready |= (1<<channel);
234+
__enable_irq();
222235
pin_high(PIN_BRIDGE_IRQ);
223236
}
224237

225238
void bridge_enable_chan(u8 channel) {
239+
__disable_irq();
226240
out_chan_ready |= (0x10<<channel);
241+
__enable_irq();
227242
pin_high(PIN_BRIDGE_IRQ);
228243
}
229244

230245
void bridge_disable_chan(u8 channel) {
246+
__disable_irq();
231247
out_chan_ready &= ~(0x11<<channel); // Also clears the "ready to accept data" bit
232248
in_chan_size[channel] = 0; // Clears any data that was waiting to be sent
249+
__enable_irq();
233250
pin_high(PIN_BRIDGE_IRQ);
234251
}

Diff for: firmware/firmware.h

+42-3
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,16 @@ void flash_usb_in_completion();
5959
void flash_usb_out_completion();
6060
void flash_disable();
6161

62+
#define FLASH_BUFFER_SIZE 512
63+
extern u8 flash_buffer[FLASH_BUFFER_SIZE];
64+
6265
// bridge.c
6366

6467
#define BRIDGE_NUM_CHAN 3
6568
#define BRIDGE_USB 0
6669
#define BRIDGE_PORT_A 1
6770
#define BRIDGE_PORT_B 2
68-
#define BRIDGE_BUF_SIZE 256
71+
#define BRIDGE_BUF_SIZE 255
6972
#define BRIDGE_ARG_SIZE 5
7073

7174
void bridge_init();
@@ -114,25 +117,61 @@ typedef struct UartBuf {
114117
} UartBuf;
115118

116119
typedef struct PortData {
120+
/// Pin mappings
117121
const TesselPort* port;
122+
123+
/// Buffers for data from the host
118124
USB_ALIGN u8 cmd_buf[BRIDGE_BUF_SIZE];
125+
126+
/// Buffers for data to the host
119127
USB_ALIGN u8 reply_buf[BRIDGE_BUF_SIZE];
128+
129+
/// Bridge channel
120130
u8 chan;
131+
132+
/// DMA channel for TX
121133
DmaChan dma_tx;
134+
135+
/// DMA channel for RX
122136
DmaChan dma_rx;
137+
138+
/// Parser state (PortState in port.c)
123139
u8 state;
140+
141+
/// Port mode (SPI/UART/etc, PortMode in port.c)
124142
u8 mode;
143+
144+
/// Length of valid data in cmd_buf
125145
u8 cmd_len;
146+
147+
/// Current position in cmd_buf
126148
u8 cmd_pos;
149+
150+
/// Current write position in reply_buf (length of valid data written)
127151
u8 reply_len;
152+
153+
/// Currently executing command (PortCmd in port.c)
128154
u8 cmd;
155+
156+
/// Parsed arguments
129157
u8 arg[BRIDGE_ARG_SIZE];
158+
159+
/// Length of arguments
130160
u8 arg_len;
161+
162+
/// Position into arguments
131163
u8 arg_pos;
132-
u8 len;
164+
165+
/// GCLK channel for this port
133166
u8 clock_channel;
167+
168+
/// TCC channel for this port
134169
u8 tcc_channel;
170+
171+
/// True if the port is waiting for a packet from the host
135172
bool pending_out;
173+
174+
/// True if the port is sending a packet to the host
136175
bool pending_in;
137176
UartBuf uart_buf;
138177
} PortData;
@@ -147,7 +186,7 @@ void port_bridge_out_completion(PortData* p, u8 len);
147186
void port_bridge_in_completion(PortData* p);
148187
void port_dma_rx_completion(PortData* p);
149188
void port_dma_tx_completion(PortData* p);
150-
void bridge_handle_sercom_uart_i2c(PortData* p);
189+
void port_handle_sercom_uart_i2c(PortData* p);
151190
void port_handle_extint(PortData *p, u32 flags);
152191
void port_disable(PortData *p);
153192
void uart_send_data(PortData *p);

Diff for: firmware/flash.c

+1-4
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@
2121
// the SPI bus.
2222
// 4. `in_count` bytes are read from SPI and sent to the IN endpoint
2323

24-
// Buffers are shared with the port, because they are not used simultaneously
25-
#define FLASH_BUFFER_SIZE BRIDGE_BUF_SIZE*2
26-
u8* const flash_buffer = port_a.cmd_buf;
2724
u32 flash_in_count;
2825
u32 flash_out_count;
2926
bool flash_flag_sr_poll;
@@ -62,7 +59,7 @@ void flash_init() {
6259
sercom_spi_master_init(SERCOM_BRIDGE, FLASH_DIPO, FLASH_DOPO, 0, 0, SERCOM_SPI_BAUD_12MHZ);
6360
dma_sercom_configure_tx(DMA_FLASH_TX, SERCOM_BRIDGE);
6461
dma_sercom_configure_rx(DMA_FLASH_RX, SERCOM_BRIDGE);
65-
DMAC->CHINTENSET.reg = DMAC_CHINTENSET_TCMPL | DMAC_CHINTENSET_TERR; // ID depends on prev call
62+
dma_enable_interrupt(DMA_FLASH_RX);
6663

6764
pin_low(PIN_SOC_RST);
6865
pin_out(PIN_SOC_RST);

Diff for: firmware/main.c

+19-15
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,23 @@
33
PortData port_a;
44
PortData port_b;
55

6+
// TODO: flash buffer could be shared with the port, because they are not used simultaneously
7+
USB_ALIGN u8 flash_buffer[FLASH_BUFFER_SIZE];
8+
69
// Indicates whether the SPI Daemon is listening for USB traffic
710
volatile bool booted = false;
811
// LED Chan: TCC1/WO[0]
912
#define PWR_LED_TCC_CHAN 1
1013
// CC channel 0 on TCC instance 1
1114
#define PWR_LED_CC_CHAN 0
12-
// The maximum counter value was chosen to get a 2s period heartbeat
13-
#define MAX_COUNTER 0xFFFF
15+
// The maximum counter value of both the TCC and the pattern counter
16+
#define MAX_COUNTER 0x10000
1417
// We have 16 slices of a sine wave
1518
#define NUM_POINT_SLICES 0x10
19+
// Pattern period in millisecond
20+
#define PATTERN_PERIOD_MS 1000
21+
#define TICKS_PER_MS 48000
22+
1623
// Number of loop iterations in a single slice
1724
#define COUNTS_IN_SLICE MAX_COUNTER/NUM_POINT_SLICES
1825
// Evenly spaced points along a sine wave, shifted up by 1, scaled by 0.5
@@ -71,15 +78,19 @@ uint32_t interpolate(uint32_t position) {
7178
Handler for the POWER LED breathing animation
7279
*/
7380
void TCC1_Handler() {
81+
tcc(PWR_LED_TCC_CHAN)->INTFLAG.reg = TCC_INTFLAG_OVF;
82+
7483
// booted is true when the coprocess first gets
7584
// a status packet from the spi daemon
7685
if (booted == true) {
7786
// Stop this breathing animation and cancel interrupts
7887
cancel_breathing_animation();
7988
}
8089

90+
counter += MAX_COUNTER / PATTERN_PERIOD_MS * MAX_COUNTER / TICKS_PER_MS;
91+
8192
// Take that proportion and extract a point along the sudo sine wave
82-
tcc(PWR_LED_TCC_CHAN)->CCB[PWR_LED_CC_CHAN].bit.CCB = interpolate(++counter);
93+
tcc(PWR_LED_TCC_CHAN)->CCB[PWR_LED_CC_CHAN].bit.CCB = interpolate(counter);
8394
}
8495

8596
/*
@@ -95,15 +106,11 @@ void init_breathing_animation() {
95106

96107
// Reset the TCC
97108
tcc(PWR_LED_TCC_CHAN)->CTRLA.reg = TCC_CTRLA_SWRST;
109+
while (tcc(PWR_LED_TCC_CHAN)->SYNCBUSY.reg != 0);
98110

99111
// Enable the timer
100112
timer_clock_enable(PWR_LED_TCC_CHAN);
101113

102-
/* Set the prescalar setting to the highest division so we have more time
103-
in between interrupts to complete the math
104-
*/
105-
tcc(PWR_LED_TCC_CHAN)->CTRLA.bit.PRESCALER = TCC_CTRLA_PRESCALER_DIV1024_Val;
106-
107114
// Set the waveform generator to generate a PWM signal
108115
// It uses polarity setting of 1 (switches from DIR to ~DIR)
109116
tcc(PWR_LED_TCC_CHAN)->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM | TCC_WAVE_POL0;
@@ -112,10 +119,7 @@ void init_breathing_animation() {
112119
tcc(PWR_LED_TCC_CHAN)->PER.reg = MAX_COUNTER;
113120

114121
// Set the counter number, starting at 0% duty cycle
115-
tcc(PWR_LED_TCC_CHAN)->CC[PWR_LED_CC_CHAN].reg = counter;
116-
117-
// Set the second CCB value value be dark for simplicity
118-
tcc(PWR_LED_TCC_CHAN)->CCB[PWR_LED_CC_CHAN].bit.CCB = counter;
122+
tcc(PWR_LED_TCC_CHAN)->CC[PWR_LED_CC_CHAN].reg = 0;
119123

120124
// Enable IRQ's in the NVIC
121125
NVIC_EnableIRQ(TCC1_IRQn);
@@ -128,7 +132,7 @@ void init_breathing_animation() {
128132
while (tcc(PWR_LED_TCC_CHAN)->SYNCBUSY.reg > 0);
129133

130134
// Enable the TCC
131-
tcc(PWR_LED_TCC_CHAN)->CTRLA.reg = TCC_CTRLA_ENABLE;
135+
tcc(PWR_LED_TCC_CHAN)->CTRLA.bit.ENABLE = 1;
132136
}
133137

134138
void boot_delay_ms(int delay){
@@ -285,11 +289,11 @@ void EVSYS_Handler() {
285289
}
286290

287291
void SERCOM_HANDLER(SERCOM_PORT_A_UART_I2C) {
288-
bridge_handle_sercom_uart_i2c(&port_a);
292+
port_handle_sercom_uart_i2c(&port_a);
289293
}
290294

291295
void SERCOM_HANDLER(SERCOM_PORT_B_UART_I2C) {
292-
bridge_handle_sercom_uart_i2c(&port_b);
296+
port_handle_sercom_uart_i2c(&port_b);
293297
}
294298

295299
void bridge_open_0() {}

0 commit comments

Comments
 (0)