From 6d460e83bdec8afac4cf36b8147b9b8dd245fb4b Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Sun, 30 Jun 2024 17:43:28 +0200 Subject: [PATCH] reset improvement --- fw/rtl/mcu/mcu_top.sv | 20 ++++---- fw/rtl/usb/usb_ft1248.sv | 62 +++++++++++++---------- sw/controller/src/fpga.h | 5 +- sw/controller/src/usb.c | 95 +++++++++++++++++++++++------------- sw/deployer/src/sc64/ftdi.rs | 49 +++++++++++++++++-- sw/deployer/src/sc64/link.rs | 14 +++--- 6 files changed, 160 insertions(+), 85 deletions(-) diff --git a/fw/rtl/mcu/mcu_top.sv b/fw/rtl/mcu/mcu_top.sv index a0540d5..41b9627 100644 --- a/fw/rtl/mcu/mcu_top.sv +++ b/fw/rtl/mcu/mcu_top.sv @@ -394,13 +394,13 @@ module mcu_top ( REG_USB_SCR: begin reg_rdata <= { - 2'd0, + 1'd0, + usb_scb.fifo_flush_busy, usb_scb.pwrsav, usb_scb.reset_state, usb_scb.tx_count, usb_scb.rx_count, - 2'b00, - usb_scb.reset_pending, + 3'b000, ~fifo_bus.tx_full, ~fifo_bus.rx_empty, 1'b0 @@ -681,9 +681,10 @@ module mcu_top ( mem_start <= 1'b0; mem_stop <= 1'b0; - usb_scb.write_buffer_flush <= 1'b0; - usb_scb.reset_ack <= 1'b0; usb_scb.fifo_flush <= 1'b0; + usb_scb.write_buffer_flush <= 1'b0; + usb_scb.reset_on_ack <= 1'b0; + usb_scb.reset_off_ack <= 1'b0; usb_dma_scb.start <= 1'b0; usb_dma_scb.stop <= 1'b0; @@ -770,11 +771,10 @@ module mcu_top ( end REG_USB_SCR: begin - { - usb_scb.write_buffer_flush, - usb_scb.reset_ack, - usb_scb.fifo_flush - } <= {reg_wdata[5:4], reg_wdata[0]}; + usb_scb.write_buffer_flush <= reg_wdata[5]; + usb_scb.reset_off_ack <= reg_wdata[4]; + usb_scb.reset_on_ack <= reg_wdata[3]; + usb_scb.fifo_flush <= reg_wdata[0]; end REG_USB_DMA_ADDRESS: begin diff --git a/fw/rtl/usb/usb_ft1248.sv b/fw/rtl/usb/usb_ft1248.sv index 9b2153b..d60d8ad 100644 --- a/fw/rtl/usb/usb_ft1248.sv +++ b/fw/rtl/usb/usb_ft1248.sv @@ -1,34 +1,37 @@ interface usb_scb (); logic fifo_flush; - logic reset_pending; - logic reset_ack; + logic fifo_flush_busy; logic write_buffer_flush; logic [10:0] rx_count; logic [10:0] tx_count; logic pwrsav; logic reset_state; + logic reset_on_ack; + logic reset_off_ack; modport controller ( output fifo_flush, - input reset_pending, - output reset_ack, + input fifo_flush_busy, output write_buffer_flush, input rx_count, input tx_count, input pwrsav, - input reset_state + input reset_state, + output reset_on_ack, + output reset_off_ack ); modport usb ( input fifo_flush, - output reset_pending, - input reset_ack, + output fifo_flush_busy, input write_buffer_flush, output rx_count, output tx_count, output pwrsav, - output reset_state + output reset_state, + input reset_on_ack, + input reset_off_ack ); endinterface @@ -59,9 +62,11 @@ module usb_ft1248 ( logic tx_read; logic [7:0] tx_rdata; + logic fifo_flush; + fifo_8kb fifo_8kb_rx_inst ( .clk(clk), - .reset(reset || usb_scb.fifo_flush), + .reset(reset || fifo_flush), .empty(fifo_bus.rx_empty), .almost_empty(fifo_bus.rx_almost_empty), @@ -78,7 +83,7 @@ module usb_ft1248 ( fifo_8kb fifo_8kb_tx_inst ( .clk(clk), - .reset(reset || usb_scb.fifo_flush), + .reset(reset || fifo_flush), .empty(tx_empty), .almost_empty(tx_almost_empty), @@ -142,7 +147,6 @@ module usb_ft1248 ( logic [3:0] phase; logic last_tx_failed; logic reset_reply; - logic last_reset_status; logic [4:0] modem_status_counter; logic write_modem_status_pending; logic write_buffer_flush_pending; @@ -152,7 +156,7 @@ module usb_ft1248 ( cmd <= next_cmd; usb_scb.pwrsav <= !ft_pwrsav; - usb_scb.reset_state <= last_reset_status; + fifo_flush <= 1'b0; phase <= {phase[2:0], phase[3]}; if (state == STATE_IDLE) begin @@ -160,25 +164,38 @@ module usb_ft1248 ( end if (reset) begin + usb_scb.fifo_flush_busy <= 1'b0; + usb_scb.reset_state <= 1'b0; last_tx_failed <= 1'b0; - usb_scb.reset_pending <= 1'b0; - last_reset_status <= 1'b0; + reset_reply <= 1'b0; modem_status_counter <= 5'd0; - write_modem_status_pending <= 1'b0; + write_modem_status_pending <= 1'b1; write_buffer_flush_pending <= 1'b0; end else begin - if (usb_scb.reset_ack) begin - usb_scb.reset_pending <= 1'b0; + if (usb_scb.fifo_flush) begin + usb_scb.fifo_flush_busy <= 1'b1; + end + + if (usb_scb.reset_on_ack) begin reset_reply <= 1'b1; write_modem_status_pending <= 1'b1; end + if (usb_scb.reset_off_ack) begin + reset_reply <= 1'b0; + write_modem_status_pending <= 1'b1; + end + if (usb_scb.write_buffer_flush) begin write_buffer_flush_pending <= 1'b1; end if (state == STATE_IDLE) begin modem_status_counter <= modem_status_counter + 1'd1; + if (usb_scb.fifo_flush_busy) begin + usb_scb.fifo_flush_busy <= 1'b0; + fifo_flush <= 1'b1; + end end if ((state == STATE_DATA) && (cmd == CMD_WRITE) && phase[3]) begin @@ -187,14 +204,7 @@ module usb_ft1248 ( if (!ft_miso && (state == STATE_DATA) && phase[3]) begin if (cmd == CMD_READ_MODEM_STATUS) begin - last_reset_status <= ft_miosi_in[0]; - if (!last_reset_status && ft_miosi_in[0]) begin - usb_scb.reset_pending <= 1'b1; - end - if (last_reset_status && !ft_miosi_in[0]) begin - reset_reply <= 1'b0; - write_modem_status_pending <= 1'b1; - end + usb_scb.reset_state <= ft_miosi_in[0]; end if (cmd == CMD_WRITE_MODEM_STATUS) begin write_modem_status_pending <= 1'b0; @@ -283,7 +293,7 @@ module usb_ft1248 ( end else begin case (state) STATE_IDLE: begin - if (ft_pwrsav) begin + if (ft_pwrsav && !(usb_scb.fifo_flush || usb_scb.fifo_flush_busy || fifo_flush)) begin if (write_modem_status_pending) begin next_state = STATE_SELECT; next_cmd = CMD_WRITE_MODEM_STATUS; diff --git a/sw/controller/src/fpga.h b/sw/controller/src/fpga.h index 7c3f0d5..652cf54 100644 --- a/sw/controller/src/fpga.h +++ b/sw/controller/src/fpga.h @@ -79,8 +79,8 @@ typedef enum { #define USB_SCR_FIFO_FLUSH (1 << 0) #define USB_SCR_RXNE (1 << 1) #define USB_SCR_TXE (1 << 2) -#define USB_SCR_RESET_PENDING (1 << 3) -#define USB_SCR_RESET_ACK (1 << 4) +#define USB_SCR_RESET_ON_ACK (1 << 3) +#define USB_SCR_RESET_OFF_ACK (1 << 4) #define USB_SCR_WRITE_FLUSH (1 << 5) #define USB_SCR_RX_COUNT_BIT (6) #define USB_SCR_RX_COUNT_MASK (0x7FF << USB_SCR_RX_COUNT_BIT) @@ -88,6 +88,7 @@ typedef enum { #define USB_SCR_TX_COUNT_MASK (0x7FF << USB_SCR_TX_COUNT_BIT) #define USB_SCR_RESET_STATE (1 << 28) #define USB_SCR_PWRSAV (1 << 29) +#define USB_SCR_FIFO_FLUSH_BUSY (1 << 30) #define DMA_SCR_START (1 << 0) #define DMA_SCR_STOP (1 << 1) diff --git a/sw/controller/src/usb.c b/sw/controller/src/usb.c index 760f68d..e66d054 100644 --- a/sw/controller/src/usb.c +++ b/sw/controller/src/usb.c @@ -43,6 +43,8 @@ enum tx_state { struct process { + bool last_reset_state; + enum rx_state rx_state; uint8_t rx_counter; uint8_t rx_cmd; @@ -80,10 +82,6 @@ static const uint32_t ERR_TOKEN = (0x45525200UL); static const uint32_t PKT_TOKEN = (0x504B5400UL); -static bool usb_dma_ready (void) { - return !((fpga_reg_get(REG_USB_DMA_SCR) & DMA_SCR_BUSY)); -} - static bool usb_rx_byte (uint8_t *data) { if (fpga_usb_status_get() & USB_STATUS_RXNE) { *data = fpga_usb_pop(); @@ -149,6 +147,59 @@ static bool usb_rx_cmd (uint8_t *cmd) { return false; } +static void usb_reset (void) { + fpga_reg_set(REG_USB_DMA_SCR, DMA_SCR_STOP); + while (fpga_reg_get(REG_USB_DMA_SCR) & DMA_SCR_BUSY); + fpga_reg_set(REG_USB_SCR, USB_SCR_FIFO_FLUSH); + while (fpga_reg_get(REG_USB_SCR) & USB_SCR_FIFO_FLUSH_BUSY); + + p.rx_state = RX_STATE_IDLE; + p.tx_state = TX_STATE_IDLE; + + p.response_pending = false; + p.packet_pending = false; + + p.read_ready = true; + p.read_length = 0; + p.read_address = 0; + + usb_rx_word_counter = 0; + usb_rx_word_buffer = 0; + usb_tx_word_counter = 0; + usb_rx_cmd_counter = 0; +} + +static void usb_flush_packet (void) { + if (p.packet_pending && p.packet_info.done_callback) { + p.packet_pending = false; + p.packet_info.done_callback(); + } + + if (p.tx_state != TX_STATE_IDLE && p.tx_info.done_callback) { + p.tx_info.done_callback(); + } +} + +static bool usb_is_active (void) { + uint32_t scr = fpga_reg_get(REG_USB_SCR); + bool reset_state = (scr & USB_SCR_RESET_STATE); + if (p.last_reset_state != reset_state) { + p.last_reset_state = reset_state; + if (reset_state) { + usb_flush_packet(); + usb_reset(); + fpga_reg_set(REG_USB_SCR, USB_SCR_WRITE_FLUSH); + } + fpga_reg_set(REG_USB_SCR, reset_state ? USB_SCR_RESET_ON_ACK : USB_SCR_RESET_OFF_ACK); + return false; + } + return !(reset_state || (scr & USB_SCR_PWRSAV)); +} + +static bool usb_dma_ready (void) { + return !((fpga_reg_get(REG_USB_DMA_SCR) & DMA_SCR_BUSY)); +} + static bool usb_validate_address_length (uint32_t address, uint32_t length, bool exclude_bootloader) { if ((address >= MEMORY_LENGTH) || (length > MEMORY_LENGTH)) { return true; @@ -556,42 +607,16 @@ void usb_get_read_info (uint32_t *args) { void usb_init (void) { - fpga_reg_set(REG_USB_DMA_SCR, DMA_SCR_STOP); - fpga_reg_set(REG_USB_SCR, USB_SCR_FIFO_FLUSH); - - p.rx_state = RX_STATE_IDLE; - p.tx_state = TX_STATE_IDLE; - - p.response_pending = false; - p.packet_pending = false; - - p.read_ready = true; - p.read_length = 0; - p.read_address = 0; - - usb_rx_word_counter = 0; - usb_rx_word_buffer = 0; - usb_tx_word_counter = 0; - usb_rx_cmd_counter = 0; + p.last_reset_state = false; + usb_reset(); } void usb_process (void) { - uint32_t scr = fpga_reg_get(REG_USB_SCR); - if (scr & (USB_SCR_PWRSAV | USB_SCR_RESET_STATE | USB_SCR_RESET_PENDING)) { - if (p.packet_pending && p.packet_info.done_callback) { - p.packet_pending = false; - p.packet_info.done_callback(); - } - if (scr & USB_SCR_RESET_PENDING) { - if (p.tx_state != TX_STATE_IDLE && p.tx_info.done_callback) { - p.tx_info.done_callback(); - } - usb_init(); - fpga_reg_set(REG_USB_SCR, USB_SCR_RESET_ACK); - } - } else { + if (usb_is_active()) { usb_rx_process(); usb_tx_process(); + } else { + usb_flush_packet(); } } diff --git a/sw/deployer/src/sc64/ftdi.rs b/sw/deployer/src/sc64/ftdi.rs index 5f76014..d5518e8 100644 --- a/sw/deployer/src/sc64/ftdi.rs +++ b/sw/deployer/src/sc64/ftdi.rs @@ -27,6 +27,7 @@ struct Wrapper { context: *mut libftdi1_sys::ftdi_context, unclog_buffer: std::collections::VecDeque, write_buffer: Vec, + read_timeout: std::time::Duration, write_timeout: std::time::Duration, read_chunksize: usize, write_chunksize: usize, @@ -49,6 +50,7 @@ impl Wrapper { context, unclog_buffer: std::collections::VecDeque::new(), write_buffer: vec![], + read_timeout: Wrapper::DEFAULT_RW_TIMEOUT, write_timeout: Wrapper::DEFAULT_RW_TIMEOUT, read_chunksize: Wrapper::DEFAULT_CHUNKSIZE, write_chunksize: Wrapper::DEFAULT_CHUNKSIZE, @@ -158,6 +160,7 @@ impl Wrapper { (*self.context).usb_write_timeout = i32::try_from(write_timeout.as_millis()) .map_err(|_| std::io::ErrorKind::InvalidInput)?; } + self.read_timeout = read_timeout; self.write_timeout = write_timeout; Ok(()) } @@ -221,6 +224,17 @@ impl Wrapper { } } + fn usb_reset(&mut self) -> std::io::Result<()> { + match unsafe { libftdi1_sys::ftdi_usb_reset(self.context) } { + 0 => Ok(()), + -1 => Err(std::io::ErrorKind::BrokenPipe.into()), + -2 => Err(std::io::ErrorKind::NotConnected.into()), + result => Err(std::io::Error::other(format!( + "Unexpected response from ftdi_usb_reset: {result}" + ))), + } + } + fn set_latency_timer(&mut self, latency: Option) -> std::io::Result<()> { let latency = u8::try_from(latency.unwrap_or(Wrapper::DEFAULT_POLL_TIMEOUT).as_millis()) .map_err(|_| std::io::ErrorKind::InvalidInput)?; @@ -300,14 +314,33 @@ impl Wrapper { } } - fn tcioflush(&mut self) -> std::io::Result<()> { - match unsafe { libftdi1_sys::ftdi_tcioflush(self.context) } { + fn tciflush(&mut self) -> std::io::Result<()> { + let timeout = std::time::Instant::now(); + loop { + match self.read(&mut vec![0u8; self.read_chunksize]) { + Ok(_) => {} + Err(error) => match error.kind() { + std::io::ErrorKind::Interrupted | std::io::ErrorKind::WouldBlock => { + return Ok(()); + } + _ => return Err(error), + }, + }; + if timeout.elapsed() > self.read_timeout { + return Err(std::io::ErrorKind::TimedOut.into()); + } + } + } + + fn tcoflush(&mut self) -> std::io::Result<()> { + self.write_buffer.clear(); + match unsafe { libftdi1_sys::ftdi_tcoflush(self.context) } { 0 => Ok(()), -1 => Err(std::io::ErrorKind::BrokenPipe.into()), -2 => Err(std::io::ErrorKind::BrokenPipe.into()), -3 => Err(std::io::ErrorKind::NotConnected.into()), result => Err(std::io::Error::other(format!( - "Unexpected response from ftdi_tcioflush: {result}" + "Unexpected response from ftdi_tcoflush: {result}" ))), } } @@ -443,6 +476,8 @@ impl FtdiDevice { wrapper.usb_open_string(description)?; + wrapper.usb_reset()?; + wrapper.set_latency_timer(poll_timeout)?; Ok(FtdiDevice { wrapper }) @@ -456,8 +491,12 @@ impl FtdiDevice { Ok(self.wrapper.poll_modem_status()?.dsr) } - pub fn discard_buffers(&mut self) -> std::io::Result<()> { - self.wrapper.tcioflush() + pub fn discard_input(&mut self) -> std::io::Result<()> { + self.wrapper.tciflush() + } + + pub fn discard_output(&mut self) -> std::io::Result<()> { + self.wrapper.tcoflush() } } diff --git a/sw/deployer/src/sc64/link.rs b/sw/deployer/src/sc64/link.rs index 47e5c28..44b1f53 100644 --- a/sw/deployer/src/sc64/link.rs +++ b/sw/deployer/src/sc64/link.rs @@ -1,5 +1,4 @@ use super::{error::Error, ftdi::FtdiDevice}; -use serial2::SerialPort; use std::{ collections::VecDeque, fmt::Display, @@ -197,7 +196,7 @@ pub trait Backend { } pub struct SerialBackend { - device: SerialPort, + device: serial2::SerialPort, } impl Backend for SerialBackend { @@ -250,7 +249,7 @@ impl Backend for SerialBackend { } fn new_serial_backend(port: &str) -> std::io::Result { - let mut serial = SerialPort::open(port, 115_200)?; + let mut serial = serial2::SerialPort::open(port, 115_200)?; serial.set_read_timeout(POLL_TIMEOUT)?; serial.set_write_timeout(WRITE_TIMEOUT)?; Ok(SerialBackend { device: serial }) @@ -274,10 +273,11 @@ impl Backend for FtdiBackend { } fn reset(&mut self) -> std::io::Result<()> { - self.device.set_dtr(true)?; + self.device.discard_output()?; + let timeout = Instant::now(); + self.device.set_dtr(true)?; loop { - self.device.discard_buffers()?; if self.device.read_dsr()? { break; } @@ -289,10 +289,10 @@ impl Backend for FtdiBackend { } } - self.purge_incoming_data()?; + self.device.discard_input()?; - self.device.set_dtr(false)?; let timeout = Instant::now(); + self.device.set_dtr(false)?; loop { if !self.device.read_dsr()? { break;