diff --git a/fw/project/lcmxo2/sc64.ldf b/fw/project/lcmxo2/sc64.ldf
index f3198e8..5d2cc62 100644
--- a/fw/project/lcmxo2/sc64.ldf
+++ b/fw/project/lcmxo2/sc64.ldf
@@ -3,6 +3,9 @@
+
@@ -12,6 +15,9 @@
+
diff --git a/fw/project/lcmxo2/sc64.lpf b/fw/project/lcmxo2/sc64.lpf
index 6feb8ab..3fd38a2 100644
--- a/fw/project/lcmxo2/sc64.lpf
+++ b/fw/project/lcmxo2/sc64.lpf
@@ -214,7 +214,7 @@ LOCATE COMP "usb_miso" SITE "10" ;
LOCATE COMP "usb_pwrsav" SITE "2" ;
SYSCONFIG SDM_PORT=DISABLE I2C_PORT=ENABLE ;
VOLTAGE 3.300 V;
-FREQUENCY NET "clk" 100.000000 MHz PAR_ADJ 10.000000 ;
+FREQUENCY NET "clk" 100.000000 MHz ;
BLOCK PATH TO PORT "mcu_int" ;
BLOCK PATH TO PORT "n64_irq" ;
BLOCK PATH FROM PORT "usb_pwrsav" ;
diff --git a/fw/rtl/fifo/fifo_bus.sv b/fw/rtl/fifo/fifo_bus.sv
index 80ca607..9d3c17f 100644
--- a/fw/rtl/fifo/fifo_bus.sv
+++ b/fw/rtl/fifo/fifo_bus.sv
@@ -1,35 +1,29 @@
interface fifo_bus ();
logic rx_empty;
- logic rx_almost_empty;
logic rx_read;
logic [7:0] rx_rdata;
logic tx_full;
- logic tx_almost_full;
logic tx_write;
logic [7:0] tx_wdata;
modport controller (
input rx_empty,
- input rx_almost_empty,
output rx_read,
input rx_rdata,
input tx_full,
- input tx_almost_full,
output tx_write,
output tx_wdata
);
modport fifo (
output rx_empty,
- output rx_almost_empty,
input rx_read,
output rx_rdata,
output tx_full,
- output tx_almost_full,
input tx_write,
input tx_wdata
);
diff --git a/fw/rtl/fifo/fifo_junction.sv b/fw/rtl/fifo/fifo_junction.sv
index cd3ffdc..459c218 100644
--- a/fw/rtl/fifo/fifo_junction.sv
+++ b/fw/rtl/fifo/fifo_junction.sv
@@ -11,16 +11,12 @@ module fifo_junction (
dev_bus.tx_wdata = cfg_bus.tx_write ? cfg_bus.tx_wdata : dma_bus.tx_wdata;
cfg_bus.rx_empty = dev_bus.rx_empty;
- cfg_bus.rx_almost_empty = dev_bus.rx_almost_empty;
cfg_bus.rx_rdata = dev_bus.rx_rdata;
cfg_bus.tx_full = dev_bus.tx_full;
- cfg_bus.tx_almost_full = dev_bus.tx_almost_full;
dma_bus.rx_empty = dev_bus.rx_empty;
- dma_bus.rx_almost_empty = dev_bus.rx_almost_empty;
dma_bus.rx_rdata = dev_bus.rx_rdata;
dma_bus.tx_full = dev_bus.tx_full;
- dma_bus.tx_almost_full = dev_bus.tx_almost_full;
end
endmodule
diff --git a/fw/rtl/memory/dma_scb.sv b/fw/rtl/memory/dma_scb.sv
new file mode 100644
index 0000000..da8d709
--- /dev/null
+++ b/fw/rtl/memory/dma_scb.sv
@@ -0,0 +1,31 @@
+interface dma_scb ();
+
+ logic start;
+ logic stop;
+ logic busy;
+ logic direction;
+ logic byte_swap;
+ logic [26:0] starting_address;
+ logic [26:0] transfer_length;
+
+ modport controller (
+ output start,
+ output stop,
+ input busy,
+ output direction,
+ output byte_swap,
+ output starting_address,
+ output transfer_length
+ );
+
+ modport dma (
+ input start,
+ input stop,
+ output busy,
+ input direction,
+ input byte_swap,
+ input starting_address,
+ input transfer_length
+ );
+
+endinterface
diff --git a/fw/rtl/memory/memory_dma.sv b/fw/rtl/memory/memory_dma.sv
index bfd15d6..7bd529f 100644
--- a/fw/rtl/memory/memory_dma.sv
+++ b/fw/rtl/memory/memory_dma.sv
@@ -1,36 +1,3 @@
-interface dma_scb ();
-
- logic start;
- logic stop;
- logic busy;
- logic direction;
- logic byte_swap;
- logic [26:0] starting_address;
- logic [26:0] transfer_length;
-
- modport controller (
- output start,
- output stop,
- input busy,
- output direction,
- output byte_swap,
- output starting_address,
- output transfer_length
- );
-
- modport dma (
- input start,
- input stop,
- output busy,
- input direction,
- input byte_swap,
- input starting_address,
- input transfer_length
- );
-
-endinterface
-
-
module memory_dma (
input clk,
input reset,
@@ -41,227 +8,403 @@ module memory_dma (
mem_bus.controller mem_bus
);
- // DMA start/stop control
+ // Start/stop logic
logic dma_start;
logic dma_stop;
- logic dma_stop_requested;
-
- always_comb begin
- dma_start = dma_scb.start && !dma_scb.stop && !dma_scb.busy;
- dma_stop = dma_scb.stop;
- end
always_ff @(posedge clk) begin
- if (reset) begin
- dma_stop_requested <= 1'b0;
- end else begin
- if (dma_stop) begin
- dma_stop_requested <= 1'b1;
- end
- if (dma_start) begin
- dma_stop_requested <= 1'b0;
- end
+ dma_start <= 1'b0;
+ if (dma_scb.stop) begin
+ dma_stop <= 1'b1;
+ end else if (dma_scb.start) begin
+ dma_start <= 1'b1;
+ dma_stop <= 1'b0;
end
end
- // Remaining counter and FIFO enable
+ // Memory bus controller
- logic [26:0] remaining;
- logic trx_enabled;
+ typedef enum bit [1:0] {
+ MEM_BUS_STATE_IDLE,
+ MEM_BUS_STATE_WAIT,
+ MEM_BUS_STATE_TRANSFER
+ } e_mem_bus_state;
+
+ e_mem_bus_state mem_bus_state;
+ e_mem_bus_state next_mem_bus_state;
+
+ logic mem_bus_wdata_ready;
+ logic mem_bus_wdata_empty;
+ logic mem_bus_wdata_end;
+ logic [1:0] mem_bus_wdata_valid;
+ logic [15:0] mem_bus_wdata_buffer;
+
+ logic mem_bus_rdata_ready;
+ logic mem_bus_rdata_ack;
+ logic mem_bus_rdata_end;
+ logic [1:0] mem_bus_rdata_valid;
+ logic [15:0] mem_bus_rdata_buffer;
+
+ logic [26:0] mem_bus_remaining_bytes;
+ logic mem_bus_last_transfer;
+ logic mem_bus_almost_last_transfer;
+ logic mem_bus_unaligned_start;
+ logic mem_bus_unaligned_end;
+
+ always_ff @(posedge clk) begin
+ if (reset) begin
+ mem_bus_state <= MEM_BUS_STATE_IDLE;
+ end else begin
+ mem_bus_state <= next_mem_bus_state;
+ end
+ end
always_comb begin
- trx_enabled = remaining > 27'd0;
+ next_mem_bus_state = mem_bus_state;
+
+ mem_bus_last_transfer = (mem_bus_remaining_bytes == 27'd0);
+ mem_bus_almost_last_transfer = (mem_bus_remaining_bytes <= 27'd2);
+
+ mem_bus_wdata_end = mem_bus_last_transfer;
+ mem_bus_wdata_valid = (mem_bus_unaligned_end && mem_bus_almost_last_transfer) ? 2'b10 :
+ mem_bus_unaligned_start ? 2'b01 :
+ 2'b11;
+
+ case (mem_bus_state)
+ MEM_BUS_STATE_IDLE: begin
+ if (dma_start) begin
+ next_mem_bus_state = MEM_BUS_STATE_WAIT;
+ end
+ end
+
+ MEM_BUS_STATE_WAIT: begin
+ if (dma_stop || mem_bus_last_transfer) begin
+ next_mem_bus_state = MEM_BUS_STATE_IDLE;
+ end else if (mem_bus_wdata_ready || !mem_bus_wdata_empty || mem_bus_rdata_ready) begin
+ next_mem_bus_state = MEM_BUS_STATE_TRANSFER;
+ end
+ end
+
+ MEM_BUS_STATE_TRANSFER: begin
+ if (mem_bus.ack) begin
+ if (dma_stop || mem_bus_last_transfer) begin
+ next_mem_bus_state = MEM_BUS_STATE_IDLE;
+ end else if (!(mem_bus_wdata_ready || !mem_bus_wdata_empty || mem_bus_rdata_ready)) begin
+ next_mem_bus_state = MEM_BUS_STATE_WAIT;
+ end
+ end
+ end
+
+ default: begin
+ next_mem_bus_state = MEM_BUS_STATE_IDLE;
+ end
+ endcase
+ end
+
+ always_ff @(posedge clk) begin
+ mem_bus_rdata_ack <= 1'b0;
+
+ if (mem_bus.ack) begin
+ mem_bus.request <= 1'b0;
+ mem_bus.address <= (mem_bus.address + 26'd2);
+ mem_bus_rdata_ack <= 1'b1;
+ mem_bus_rdata_end <= mem_bus_last_transfer;
+ mem_bus_rdata_valid <= mem_bus.wmask;
+ mem_bus_rdata_buffer <= mem_bus.rdata;
+ end
+
+ if (mem_bus_wdata_ready) begin
+ mem_bus_wdata_empty <= 1'b0;
+ end
+
+ case (mem_bus_state)
+ MEM_BUS_STATE_IDLE: begin
+ mem_bus.request <= 1'b0;
+ mem_bus_rdata_end <= 1'b1;
+ if (dma_start) begin
+ mem_bus.write <= dma_scb.direction;
+ mem_bus.address <= {dma_scb.starting_address[26:1], 1'b0};
+ mem_bus_remaining_bytes <= dma_scb.transfer_length;
+ mem_bus_unaligned_start <= dma_scb.starting_address[0];
+ mem_bus_unaligned_end <= (dma_scb.starting_address[0] ^ dma_scb.transfer_length[0]);
+ mem_bus_rdata_end <= 1'b0;
+ mem_bus_wdata_empty <= 1'b1;
+ end
+ end
+
+ MEM_BUS_STATE_WAIT: begin
+ if (!dma_stop && !mem_bus_last_transfer) begin
+ if (mem_bus_wdata_ready || !mem_bus_wdata_empty || mem_bus_rdata_ready) begin
+ mem_bus.request <= 1'b1;
+ mem_bus_unaligned_start <= 1'b0;
+ mem_bus.wdata <= (dma_scb.byte_swap ? {mem_bus_wdata_buffer[7:0], mem_bus_wdata_buffer[15:8]} : mem_bus_wdata_buffer);
+ mem_bus.wmask <= 2'b11;
+ mem_bus_remaining_bytes <= (mem_bus_remaining_bytes - 27'd2);
+ if (mem_bus_unaligned_end && mem_bus_almost_last_transfer) begin
+ mem_bus.wmask <= 2'b10;
+ mem_bus_remaining_bytes <= (mem_bus_remaining_bytes - 27'd1);
+ end
+ if (mem_bus_unaligned_start) begin
+ mem_bus.wmask <= 2'b01;
+ mem_bus_remaining_bytes <= (mem_bus_remaining_bytes - 27'd1);
+ end
+ mem_bus_wdata_empty <= 1'b1;
+ end
+ end
+ end
+
+ MEM_BUS_STATE_TRANSFER: begin
+ if (!dma_stop && !mem_bus_last_transfer) begin
+ if (mem_bus.ack && (mem_bus_wdata_ready || !mem_bus_wdata_empty || mem_bus_rdata_ready)) begin
+ mem_bus.request <= 1'b1;
+ mem_bus.wdata <= (dma_scb.byte_swap ? {mem_bus_wdata_buffer[7:0], mem_bus_wdata_buffer[15:8]} : mem_bus_wdata_buffer);
+ mem_bus.wmask <= 2'b11;
+ mem_bus_remaining_bytes <= (mem_bus_remaining_bytes - 27'd2);
+ if (mem_bus_unaligned_end && mem_bus_almost_last_transfer) begin
+ mem_bus.wmask <= 2'b10;
+ mem_bus_remaining_bytes <= (mem_bus_remaining_bytes - 27'd1);
+ end
+ mem_bus_wdata_empty <= 1'b1;
+ end
+ end
+ end
+
+ default: begin end
+ endcase
end
// RX FIFO controller
- logic [1:0] rx_wmask;
- logic rx_rdata_pop;
- logic rx_rdata_shift;
- logic rx_rdata_valid;
- logic [15:0] rx_buffer;
- logic rx_buffer_valid;
- logic [1:0] rx_buffer_counter;
- logic [1:0] rx_buffer_valid_counter;
+ typedef enum bit [2:0] {
+ RX_FIFO_BUS_STATE_IDLE,
+ RX_FIFO_BUS_STATE_WAIT,
+ RX_FIFO_BUS_STATE_TRANSFER_1,
+ RX_FIFO_BUS_STATE_TRANSFER_2,
+ RX_FIFO_BUS_STATE_ACK
+ } e_rx_fifo_bus_state;
+
+ e_rx_fifo_bus_state rx_fifo_bus_state;
+ e_rx_fifo_bus_state next_rx_fifo_bus_state;
+
+ logic rx_fifo_shift;
+ logic rx_fifo_shift_delayed;
+ logic [1:0] rx_fifo_valid;
+
+ always_ff @(posedge clk) begin
+ if (reset || dma_stop) begin
+ rx_fifo_bus_state <= RX_FIFO_BUS_STATE_IDLE;
+ end else begin
+ rx_fifo_bus_state <= next_rx_fifo_bus_state;
+ end
+ end
always_comb begin
- rx_buffer_valid = rx_buffer_valid_counter == 2'd2;
+ next_rx_fifo_bus_state = rx_fifo_bus_state;
+
+ rx_fifo_shift = 1'b0;
+
+ fifo_bus.rx_read = 1'b0;
+
+ case (rx_fifo_bus_state)
+ RX_FIFO_BUS_STATE_IDLE: begin
+ if (dma_start && dma_scb.direction) begin
+ next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_WAIT;
+ end
+ end
+
+ RX_FIFO_BUS_STATE_WAIT: begin
+ if (mem_bus_wdata_end) begin
+ next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_IDLE;
+ end else if (mem_bus_wdata_empty) begin
+ next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_TRANSFER_1;
+ end
+ end
+
+ RX_FIFO_BUS_STATE_TRANSFER_1: begin
+ fifo_bus.rx_read = (!fifo_bus.rx_empty && rx_fifo_valid[1]);
+ if (!fifo_bus.rx_empty || !rx_fifo_valid[1]) begin
+ next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_TRANSFER_2;
+ rx_fifo_shift = 1'b1;
+ end
+ end
+
+ RX_FIFO_BUS_STATE_TRANSFER_2: begin
+ fifo_bus.rx_read = (!fifo_bus.rx_empty && rx_fifo_valid[1]);
+ if (!fifo_bus.rx_empty || !rx_fifo_valid[1]) begin
+ next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_ACK;
+ rx_fifo_shift = 1'b1;
+ end
+ end
+
+ RX_FIFO_BUS_STATE_ACK: begin
+ if (mem_bus_wdata_ready) begin
+ next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_WAIT;
+ end
+ end
+
+ default: begin
+ next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_IDLE;
+ end
+ endcase
end
always_ff @(posedge clk) begin
- rx_rdata_pop <= (
- !rx_rdata_pop &&
- !fifo_bus.rx_read &&
- trx_enabled &&
- rx_buffer_counter < 2'd2 &&
- !fifo_bus.rx_empty &&
- mem_bus.write
- );
- rx_rdata_shift <= 1'b0;
- fifo_bus.rx_read <= rx_rdata_pop;
- rx_rdata_valid <= fifo_bus.rx_read;
+ mem_bus_wdata_ready <= 1'b0;
+ rx_fifo_shift_delayed <= rx_fifo_shift;
- if (dma_start) begin
- if (dma_scb.starting_address[0]) begin
- rx_wmask <= 2'b01;
- rx_buffer_counter <= 2'd1;
- rx_buffer_valid_counter <= 2'd1;
- end else begin
- rx_wmask <= 2'b11;
- rx_buffer_counter <= 2'd0;
- rx_buffer_valid_counter <= 2'd0;
+ if (rx_fifo_shift) begin
+ rx_fifo_valid <= {rx_fifo_valid[0], 1'bX};
+ end
+
+ if (rx_fifo_shift_delayed) begin
+ if (rx_fifo_bus_state == RX_FIFO_BUS_STATE_ACK) begin
+ mem_bus_wdata_ready <= 1'b1;
end
+ mem_bus_wdata_buffer <= {mem_bus_wdata_buffer[7:0], fifo_bus.rx_rdata};
end
- if (rx_rdata_pop) begin
- rx_buffer_counter <= rx_buffer_counter + 1'd1;
- end
-
- if (rx_rdata_shift || rx_rdata_valid) begin
- if (dma_scb.byte_swap) begin
- rx_buffer <= {fifo_bus.rx_rdata, rx_buffer[15:8]};
- end else begin
- rx_buffer <= {rx_buffer[7:0], fifo_bus.rx_rdata};
+ case (rx_fifo_bus_state)
+ RX_FIFO_BUS_STATE_WAIT: begin
+ if (mem_bus_wdata_empty) begin
+ rx_fifo_valid <= mem_bus_wdata_valid;
+ end
end
- rx_buffer_valid_counter <= rx_buffer_valid_counter + 1'd1;
- if (remaining == 27'd0 && rx_buffer_counter == 2'd1) begin
- rx_wmask <= 2'b10;
- rx_rdata_shift <= 1'b1;
- rx_buffer_counter <= rx_buffer_counter + 1'd1;
- end
- end
- if (rx_buffer_valid && !mem_bus.request) begin
- rx_wmask <= 2'b11;
- rx_buffer_counter <= 2'd0;
- rx_buffer_valid_counter <= 2'd0;
- end
+ default: begin end
+ endcase
end
// TX FIFO controller
- logic tx_wdata_push;
- logic tx_wdata_first_push;
- logic [7:0] tx_buffer;
- logic tx_buffer_counter;
- logic tx_buffer_ready;
- logic tx_buffer_valid;
+ typedef enum bit [1:0] {
+ TX_FIFO_BUS_STATE_IDLE,
+ TX_FIFO_BUS_STATE_WAIT,
+ TX_FIFO_BUS_STATE_TRANSFER_1,
+ TX_FIFO_BUS_STATE_TRANSFER_2
+ } e_tx_fifo_bus_state;
+
+ e_tx_fifo_bus_state tx_fifo_bus_state;
+ e_tx_fifo_bus_state next_tx_fifo_bus_state;
+
+ logic tx_fifo_shift;
+ logic tx_fifo_waiting;
+ logic [1:0] tx_fifo_valid;
+ logic [15:0] tx_fifo_buffer;
+
+ always_ff @(posedge clk) begin
+ if (reset || dma_stop) begin
+ tx_fifo_bus_state <= TX_FIFO_BUS_STATE_IDLE;
+ end else begin
+ tx_fifo_bus_state <= next_tx_fifo_bus_state;
+ end
+ end
always_comb begin
- fifo_bus.tx_write = tx_wdata_push;
- end
+ next_tx_fifo_bus_state = tx_fifo_bus_state;
- always_ff @(posedge clk) begin
- tx_wdata_push <= (
- !tx_wdata_push &&
- trx_enabled &&
- tx_buffer_valid &&
- !fifo_bus.tx_full &&
- !mem_bus.write
- );
+ tx_fifo_shift = 1'b0;
- if (reset || dma_stop) begin
- tx_buffer_ready <= 1'b0;
- tx_buffer_valid <= 1'b0;
- end
+ fifo_bus.tx_write = 1'b0;
+ fifo_bus.tx_wdata = tx_fifo_buffer[15:8];
- if (dma_start) begin
- tx_wdata_first_push <= 1'b1;
- tx_buffer_ready <= 1'b1;
- tx_buffer_valid <= 1'b0;
- end
-
- if (tx_buffer_ready && mem_bus.request) begin
- tx_buffer_ready <= 1'b0;
- end
-
- if (mem_bus.ack) begin
- tx_wdata_first_push <= 1'b0;
- tx_buffer_counter <= 1'd1;
- tx_buffer_valid <= 1'b1;
- {fifo_bus.tx_wdata, tx_buffer} <= mem_bus.rdata;
- if (tx_wdata_first_push && dma_scb.starting_address[0]) begin
- fifo_bus.tx_wdata <= mem_bus.rdata[7:0];
- tx_buffer_counter <= 1'd0;
- end
- end
-
- if (tx_wdata_push) begin
- tx_buffer_counter <= tx_buffer_counter - 1'd1;
- fifo_bus.tx_wdata <= tx_buffer;
- if (tx_buffer_counter == 1'd0) begin
- tx_buffer_ready <= 1'b1;
- tx_buffer_valid <= 1'b0;
- end
- end
- end
-
-
- // Remaining counter controller
-
- always_ff @(posedge clk) begin
- if (reset || dma_stop) begin
- remaining <= 27'd0;
- end else begin
- if (dma_start) begin
- remaining <= dma_scb.transfer_length;
+ case (tx_fifo_bus_state)
+ TX_FIFO_BUS_STATE_IDLE: begin
+ if (dma_start && !dma_scb.direction) begin
+ next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_WAIT;
+ end
end
- if (!dma_stop_requested && ((mem_bus.write && rx_rdata_pop) || (!mem_bus.write && tx_wdata_push))) begin
- remaining <= remaining - 1'd1;
+ TX_FIFO_BUS_STATE_WAIT: begin
+ if (mem_bus_rdata_ack || tx_fifo_waiting) begin
+ next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_TRANSFER_1;
+ end else if (mem_bus_rdata_end) begin
+ next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_IDLE;
+ end
end
- end
- end
+ TX_FIFO_BUS_STATE_TRANSFER_1: begin
+ fifo_bus.tx_write = (!fifo_bus.tx_full && tx_fifo_valid[1]);
+ if (!fifo_bus.tx_full || !tx_fifo_valid[1]) begin
+ next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_TRANSFER_2;
+ tx_fifo_shift = 1'b1;
+ end
+ end
- // Mem bus controller
-
- always_ff @(posedge clk) begin
- dma_scb.busy <= mem_bus.request || trx_enabled;
- end
-
- always_ff @(posedge clk) begin
- if (reset) begin
- mem_bus.request <= 1'b0;
- end else begin
- if (!mem_bus.request) begin
- if (mem_bus.write) begin
- if (rx_buffer_valid) begin
- mem_bus.request <= 1'b1;
- mem_bus.wmask <= rx_wmask;
- mem_bus.wdata <= rx_buffer;
- end
- end else begin
- if (tx_buffer_ready) begin
- mem_bus.request <= 1'b1;
+ TX_FIFO_BUS_STATE_TRANSFER_2: begin
+ fifo_bus.tx_write = (!fifo_bus.tx_full && tx_fifo_valid[1]);
+ if (!fifo_bus.tx_full || !tx_fifo_valid[1]) begin
+ next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_WAIT;
+ tx_fifo_shift = 1'b1;
+ if (!mem_bus_rdata_ack && !tx_fifo_waiting && mem_bus_rdata_end) begin
+ next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_IDLE;
end
end
end
- end
- if (mem_bus.ack) begin
- mem_bus.request <= 1'b0;
- end
+ default: begin
+ next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_IDLE;
+ end
+ endcase
end
always_ff @(posedge clk) begin
- if (dma_start) begin
- mem_bus.write <= dma_scb.direction;
+ if (tx_fifo_shift) begin
+ tx_fifo_valid <= {tx_fifo_valid[0], 1'bX};
+ tx_fifo_buffer <= {tx_fifo_buffer[7:0], 8'hXX};
end
+
+ case (tx_fifo_bus_state)
+ TX_FIFO_BUS_STATE_IDLE: begin
+ mem_bus_rdata_ready <= 1'b0;
+ tx_fifo_waiting <= 1'b0;
+ if (dma_start) begin
+ mem_bus_rdata_ready <= !dma_scb.direction;
+ end
+ end
+
+ TX_FIFO_BUS_STATE_WAIT: begin
+ if (mem_bus_rdata_ack || tx_fifo_waiting) begin
+ mem_bus_rdata_ready <= 1'b0;
+ tx_fifo_waiting <= 1'b0;
+ tx_fifo_valid <= mem_bus_rdata_valid;
+ tx_fifo_buffer <= mem_bus_rdata_buffer;
+ end
+ end
+
+ TX_FIFO_BUS_STATE_TRANSFER_1: begin
+ if (mem_bus_rdata_ack) begin
+ tx_fifo_waiting <= 1'b1;
+ end
+ end
+
+ TX_FIFO_BUS_STATE_TRANSFER_2: begin
+ if (mem_bus_rdata_ack) begin
+ tx_fifo_waiting <= 1'b1;
+ end
+ if (fifo_bus.tx_write || !tx_fifo_valid[1]) begin
+ mem_bus_rdata_ready <= !mem_bus_rdata_end;
+ end
+ end
+
+ default: begin end
+ endcase
end
- always_ff @(posedge clk) begin
- if (dma_start) begin
- mem_bus.address <= {dma_scb.starting_address[26:1], 1'b0};
- end
- if (mem_bus.ack) begin
- mem_bus.address <= mem_bus.address + 2'd2;
- end
+ // DMA busy indicator
+
+ always_ff @(posedge clk) begin
+ dma_scb.busy <= (
+ (dma_scb.start && !dma_scb.stop) ||
+ dma_start ||
+ (mem_bus_state != MEM_BUS_STATE_IDLE) ||
+ (rx_fifo_bus_state != RX_FIFO_BUS_STATE_IDLE) ||
+ (tx_fifo_bus_state != TX_FIFO_BUS_STATE_IDLE)
+ );
end
endmodule
diff --git a/fw/rtl/memory/memory_sdram.sv b/fw/rtl/memory/memory_sdram.sv
index fce983a..0d4179b 100644
--- a/fw/rtl/memory/memory_sdram.sv
+++ b/fw/rtl/memory/memory_sdram.sv
@@ -14,29 +14,38 @@ module memory_sdram (
inout [15:0] sdram_dq
);
- localparam [2:0] CAS_LATENCY = 3'd2;
+ // in Hz
+ localparam real FREQUENCY = 100_000_000.0;
- localparam real T_INIT = 100_000.0;
- localparam real T_RC = 60.0;
- localparam real T_RP = 15.0;
- localparam real T_RCD = 15.0;
- localparam real T_MRD = 14.0;
- localparam real T_REF = 7_800.0;
+ // in clocks
+ localparam bit [2:0] CAS_LATENCY = 3'd2;
- localparam real T_CLK = (1.0 / 100_000_000) * 1_000_000_000.0;
- localparam int C_INIT = int'((T_INIT + T_CLK - 1) / T_CLK);
- localparam int C_RC = int'((T_RC + T_CLK - 1) / T_CLK);
- localparam int C_RP = int'((T_RP + T_CLK - 1) / T_CLK);
- localparam int C_RCD = int'((T_RCD + T_CLK - 1) / T_CLK);
- localparam int C_MRD = int'((T_MRD + T_CLK - 1) / T_CLK);
- localparam int C_REF = int'((T_REF + T_CLK - 1) / T_CLK);
+ // in nanoseconds
+ localparam real T_INIT = 100_000.0;
+ localparam real T_MRD = 14.0;
+ localparam real T_RAS = 37.0;
+ localparam real T_RC = 60.0;
+ localparam real T_RCD = 15.0;
+ localparam real T_REF = 7_812.5;
+ localparam real T_RP = 15.0;
- localparam INIT_PRECHARGE = 4'd0;
- localparam INIT_REFRESH_1 = C_RP;
- localparam INIT_REFRESH_2 = C_RP + C_RC;
- localparam INIT_MODE_REG = C_RP + (2 * C_RC);
- localparam INIT_DONE = C_RP + (2 * C_RC) + C_MRD;
+ localparam real T_CLK = (1.0 / FREQUENCY) * 1_000_000_000.0;
+ const bit [13:0] C_INIT = 14'(int'($ceil(T_INIT / T_CLK)));
+ const bit [4:0] C_MRD = 5'(int'($ceil(T_MRD / T_CLK)));
+ const bit [2:0] C_RAS = 3'(int'($ceil(T_RAS / T_CLK)));
+ const bit [4:0] C_RC = 5'(int'($ceil(T_RC / T_CLK)));
+ const bit [4:0] C_RCD = 5'(int'($ceil(T_RCD / T_CLK)));
+ const bit [13:0] C_REF = 14'(int'($ceil(T_REF / T_CLK)));
+ const bit [4:0] C_RP = 5'(int'($ceil(T_RP / T_CLK)));
+
+ const bit [4:0] INIT_PRECHARGE = 5'd0;
+ const bit [4:0] INIT_REFRESH_1 = INIT_PRECHARGE + C_RP;
+ const bit [4:0] INIT_REFRESH_2 = INIT_REFRESH_1 + C_RC;
+ const bit [4:0] INIT_MODE_REG = INIT_REFRESH_2 + C_RC;
+ const bit [4:0] INIT_DONE = INIT_MODE_REG + C_MRD;
+
+ // /CS, /RAS, /CAS, /WE
typedef enum bit [3:0] {
CMD_DESL = 4'b1111,
CMD_NOP = 4'b0111,
@@ -58,13 +67,10 @@ module memory_sdram (
always_ff @(posedge clk) begin
{sdram_cs, sdram_ras, sdram_cas, sdram_we} <= 4'(sdram_next_cmd);
-
{sdram_ba, sdram_a} <= 15'd0;
sdram_dqm <= 2'b00;
-
sdram_dq_input <= sdram_dq;
sdram_dq_output <= mem_bus.wdata;
-
sdram_dq_output_enable <= 1'b0;
case (sdram_next_cmd)
@@ -76,19 +82,31 @@ module memory_sdram (
CMD_ACT: begin
{sdram_ba, sdram_a} <= mem_bus.address[25:11];
- sdram_dqm <= 2'b00;
current_active_bank_row <= mem_bus.address[25:11];
end
CMD_PRE: begin
- {sdram_ba, sdram_a} <= {2'b00, 2'b00, 1'b1, 10'd0};
- sdram_dqm <= 2'b00;
+ {sdram_ba, sdram_a} <= {
+ 2'b00, // [BA1:BA0] Don't care
+ 2'b00, // [A12:A11] Don't care
+ 1'b1, // [A10] Precharge all banks
+ 10'd0 // [A9:A0] Don't care
+ };
end
CMD_MRS: begin
- {sdram_ba, sdram_a} <= {2'b00, 1'b0, 1'b0, 2'b00, CAS_LATENCY, 1'b0, 3'b000};
- sdram_dqm <= 2'b00;
+ {sdram_ba, sdram_a} <= {
+ 2'b00, // [BA1:BA0] Reserved = 0
+ 3'b000, // [A12:A10] Reserved = 0
+ 1'b0, // [A9] Write Burst Mode = Programmed Burst Length
+ 2'b00, // [A8:A7] Operating Mode = Standard Operation
+ CAS_LATENCY, // [A6:A4] Latency Mode = 2
+ 1'b0, // [A3] Burst Type = Sequential
+ 3'b000 // [A2:A0] Burst Length = 1
+ };
end
+
+ default: begin end
endcase
end
@@ -121,35 +139,51 @@ module memory_sdram (
end
end
- logic [13:0] powerup_coutner;
- logic powerup_done;
+ logic [13:0] refresh_counter;
logic [4:0] wait_counter;
- logic [9:0] refresh_counter;
+ logic [2:0] precharge_counter;
+ logic powerup_done;
logic pending_refresh;
+ logic precharge_valid;
always_ff @(posedge clk) begin
- if (reset) begin
- powerup_coutner <= 14'd0;
- powerup_done <= 1'b0;
- end else if (powerup_coutner < C_INIT) begin
- powerup_coutner <= powerup_coutner + 1'd1;
- end else begin
+ refresh_counter <= refresh_counter + 1'd1;
+
+ if (refresh_counter == C_INIT) begin
+ refresh_counter <= 14'd0;
powerup_done <= 1'b1;
end
- if (reset || state != next_state) begin
- wait_counter <= 5'd0;
- end else begin
- wait_counter <= wait_counter + 1'd1;
+ if (powerup_done && refresh_counter == C_REF - 14'd1) begin
+ refresh_counter <= 14'd0;
+ pending_refresh <= 1'b1;
end
if (sdram_next_cmd == CMD_REF) begin
- refresh_counter <= 10'd0;
pending_refresh <= 1'b0;
- end else if (refresh_counter < C_REF) begin
- refresh_counter <= refresh_counter + 1'd1;
- end else begin
- pending_refresh <= 1'b1;
+ end
+
+ if (reset) begin
+ refresh_counter <= 14'd0;
+ powerup_done <= 1'b0;
+ pending_refresh <= 1'b0;
+ end
+
+ wait_counter <= wait_counter + 1'd1;
+
+ if (state != next_state) begin
+ wait_counter <= 5'd0;
+ end
+
+ precharge_counter <= precharge_counter + 1'd1;
+
+ if (precharge_counter >= C_RAS - 2'd2) begin
+ precharge_valid <= 1'b1;
+ end
+
+ if (sdram_next_cmd == CMD_ACT) begin
+ precharge_counter <= 3'd0;
+ precharge_valid <= 1'b0;
end
end
@@ -157,6 +191,7 @@ module memory_sdram (
always_ff @(posedge clk) begin
mem_bus.ack <= 1'b0;
+
read_cmd_ack_delay <= {sdram_next_cmd == CMD_READ, read_cmd_ack_delay[(CAS_LATENCY):1]};
if (sdram_next_cmd == CMD_WRITE || read_cmd_ack_delay[0]) begin
@@ -202,20 +237,20 @@ module memory_sdram (
end
S_ACTIVATING: begin
- if (wait_counter == C_RCD) begin
+ if (wait_counter == C_RCD - 5'd2) begin
next_state = S_ACTIVE;
end
end
S_ACTIVE: begin
- if (pending_refresh) begin
+ if (pending_refresh && precharge_valid) begin
next_state = S_PRECHARGE;
sdram_next_cmd = CMD_PRE;
end else if (mem_bus.request) begin
if (request_in_current_active_bank_row) begin
next_state = S_BUSY;
sdram_next_cmd = mem_bus.write ? CMD_WRITE : CMD_READ;
- end else begin
+ end else if (precharge_valid) begin
next_state = S_PRECHARGE;
sdram_next_cmd = CMD_PRE;
end
@@ -229,18 +264,13 @@ module memory_sdram (
end
S_PRECHARGE: begin
- if (wait_counter == C_RP) begin
- if (pending_refresh) begin
- next_state = S_REFRESH;
- sdram_next_cmd = CMD_REF;
- end else begin
- next_state = S_IDLE;
- end
+ if (wait_counter == C_RP - 5'd2) begin
+ next_state = S_IDLE;
end
end
S_REFRESH: begin
- if (wait_counter == C_RC) begin
+ if (wait_counter == C_RC - 5'd2) begin
next_state = S_IDLE;
end
end
diff --git a/fw/rtl/sd/sd_dat.sv b/fw/rtl/sd/sd_dat.sv
index 419efa8..00c88c4 100644
--- a/fw/rtl/sd/sd_dat.sv
+++ b/fw/rtl/sd/sd_dat.sv
@@ -36,12 +36,10 @@ module sd_dat (
// FIFO
logic rx_full;
- logic rx_almost_full;
logic rx_write;
logic [7:0] rx_wdata;
logic tx_empty;
- logic tx_almost_empty;
logic tx_read;
logic [7:0] tx_rdata;
@@ -50,12 +48,10 @@ module sd_dat (
.reset(reset || sd_scb.dat_fifo_flush),
.empty(fifo_bus.rx_empty),
- .almost_empty(fifo_bus.rx_almost_empty),
.read(fifo_bus.rx_read),
.rdata(fifo_bus.rx_rdata),
.full(rx_full),
- .almost_full(rx_almost_full),
.write(rx_write),
.wdata(rx_wdata),
@@ -67,12 +63,10 @@ module sd_dat (
.reset(reset || sd_scb.dat_fifo_flush),
.empty(tx_empty),
- .almost_empty(tx_almost_empty),
.read(tx_read),
.rdata(tx_rdata),
.full(fifo_bus.tx_full),
- .almost_full(fifo_bus.tx_almost_full),
.write(fifo_bus.tx_write),
.wdata(fifo_bus.tx_wdata),
diff --git a/fw/rtl/usb/usb_ft1248.sv b/fw/rtl/usb/usb_ft1248.sv
index d60d8ad..fd3f3af 100644
--- a/fw/rtl/usb/usb_ft1248.sv
+++ b/fw/rtl/usb/usb_ft1248.sv
@@ -1,42 +1,3 @@
-interface usb_scb ();
-
- logic fifo_flush;
- 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 fifo_flush_busy,
- output write_buffer_flush,
- input rx_count,
- input tx_count,
- input pwrsav,
- input reset_state,
- output reset_on_ack,
- output reset_off_ack
- );
-
- modport usb (
- input fifo_flush,
- output fifo_flush_busy,
- input write_buffer_flush,
- output rx_count,
- output tx_count,
- output pwrsav,
- output reset_state,
- input reset_on_ack,
- input reset_off_ack
- );
-
-endinterface
-
-
module usb_ft1248 (
input clk,
input reset,
@@ -53,12 +14,10 @@ module usb_ft1248 (
);
logic rx_full;
- logic rx_almost_full;
- logic rx_write;
+ logic rx_write_delayed;
logic [7:0] rx_wdata;
logic tx_empty;
- logic tx_almost_empty;
logic tx_read;
logic [7:0] tx_rdata;
@@ -66,16 +25,14 @@ module usb_ft1248 (
fifo_8kb fifo_8kb_rx_inst (
.clk(clk),
- .reset(reset || fifo_flush),
+ .reset(fifo_flush),
.empty(fifo_bus.rx_empty),
- .almost_empty(fifo_bus.rx_almost_empty),
.read(fifo_bus.rx_read),
.rdata(fifo_bus.rx_rdata),
.full(rx_full),
- .almost_full(rx_almost_full),
- .write(rx_write),
+ .write(rx_write_delayed),
.wdata(rx_wdata),
.count(usb_scb.rx_count)
@@ -83,15 +40,13 @@ module usb_ft1248 (
fifo_8kb fifo_8kb_tx_inst (
.clk(clk),
- .reset(reset || fifo_flush),
+ .reset(fifo_flush),
.empty(tx_empty),
- .almost_empty(tx_almost_empty),
.read(tx_read),
.rdata(tx_rdata),
.full(fifo_bus.tx_full),
- .almost_full(fifo_bus.tx_almost_full),
.write(fifo_bus.tx_write),
.wdata(fifo_bus.tx_wdata),
@@ -145,6 +100,8 @@ module usb_ft1248 (
e_cmd cmd;
e_cmd next_cmd;
logic [3:0] phase;
+ logic rx_write;
+ logic last_rx_failed;
logic last_tx_failed;
logic reset_reply;
logic [4:0] modem_status_counter;
@@ -158,6 +115,8 @@ module usb_ft1248 (
usb_scb.pwrsav <= !ft_pwrsav;
fifo_flush <= 1'b0;
+ rx_write_delayed <= rx_write;
+
phase <= {phase[2:0], phase[3]};
if (state == STATE_IDLE) begin
phase <= 4'b0100;
@@ -166,6 +125,7 @@ module usb_ft1248 (
if (reset) begin
usb_scb.fifo_flush_busy <= 1'b0;
usb_scb.reset_state <= 1'b0;
+ last_rx_failed <= 1'b0;
last_tx_failed <= 1'b0;
reset_reply <= 1'b0;
modem_status_counter <= 5'd0;
@@ -195,9 +155,19 @@ module usb_ft1248 (
if (usb_scb.fifo_flush_busy) begin
usb_scb.fifo_flush_busy <= 1'b0;
fifo_flush <= 1'b1;
+ last_rx_failed <= 1'b0;
+ last_tx_failed <= 1'b0;
+ end else if (last_rx_failed && !rx_full) begin
+ last_rx_failed <= 1'b0;
+ rx_write_delayed <= 1'b1;
end
end
+ if ((state == STATE_DATA) && (cmd == CMD_READ) && phase[3]) begin
+ rx_wdata <= ft_miosi_in;
+ last_rx_failed <= !ft_miso && rx_full;
+ end
+
if ((state == STATE_DATA) && (cmd == CMD_WRITE) && phase[3]) begin
last_tx_failed <= ft_miso;
end
@@ -262,8 +232,6 @@ module usb_ft1248 (
rx_write = 1'b0;
tx_read = 1'b0;
- rx_wdata = ft_miosi_in;
-
if (!ft_miso && phase[3]) begin
case (state)
STATE_STATUS: begin
@@ -273,13 +241,15 @@ module usb_ft1248 (
end
STATE_DATA: begin
- if (cmd == CMD_READ) begin
+ if (cmd == CMD_READ && !rx_full) begin
rx_write = 1'b1;
end
if (cmd == CMD_WRITE && !tx_empty) begin
tx_read = 1'b1;
end
end
+
+ default: begin end
endcase
end
end
@@ -340,7 +310,7 @@ module usb_ft1248 (
if (ft_miso) begin
next_state = STATE_DESELECT;
end else if (cmd == CMD_READ) begin
- if (rx_almost_full) begin
+ if (rx_full) begin
next_state = STATE_DESELECT;
end
end else if (cmd == CMD_WRITE) begin
diff --git a/fw/rtl/usb/usb_scb.sv b/fw/rtl/usb/usb_scb.sv
new file mode 100644
index 0000000..b2a322d
--- /dev/null
+++ b/fw/rtl/usb/usb_scb.sv
@@ -0,0 +1,37 @@
+interface usb_scb ();
+
+ logic fifo_flush;
+ 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 fifo_flush_busy,
+ output write_buffer_flush,
+ input rx_count,
+ input tx_count,
+ input pwrsav,
+ input reset_state,
+ output reset_on_ack,
+ output reset_off_ack
+ );
+
+ modport usb (
+ input fifo_flush,
+ output fifo_flush_busy,
+ input write_buffer_flush,
+ output rx_count,
+ output tx_count,
+ output pwrsav,
+ output reset_state,
+ input reset_on_ack,
+ input reset_off_ack
+ );
+
+endinterface
diff --git a/fw/rtl/vendor/lcmxo2/fifo_8kb.sv b/fw/rtl/vendor/lcmxo2/fifo_8kb.sv
index db02ad2..05fb8b3 100644
--- a/fw/rtl/vendor/lcmxo2/fifo_8kb.sv
+++ b/fw/rtl/vendor/lcmxo2/fifo_8kb.sv
@@ -3,18 +3,19 @@ module fifo_8kb (
input reset,
output empty,
- output almost_empty,
input read,
output [7:0] rdata,
output full,
- output almost_full,
input write,
input [7:0] wdata,
-
+
output logic [10:0] count
);
+ logic almost_empty;
+ logic almost_full;
+
fifo_8kb_lattice_generated fifo_8kb_lattice_generated_inst (
.Data(wdata),
.WrClock(clk),
@@ -25,7 +26,7 @@ module fifo_8kb (
.RPReset(reset),
.Q(rdata),
.Empty(empty),
- .Full(full),
+ .Full(full),
.AlmostEmpty(almost_empty),
.AlmostFull(almost_full)
);
diff --git a/fw/tests/.gitignore b/fw/tests/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/fw/tests/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/fw/tests/Makefile b/fw/tests/Makefile
new file mode 100644
index 0000000..42e11a1
--- /dev/null
+++ b/fw/tests/Makefile
@@ -0,0 +1,35 @@
+RTL_DIR = ../rtl
+BENCHES_DIR = benches
+MOCKS_DIR = mocks
+BUILD_DIR = build
+SRC_DIRS = \
+ $(RTL_DIR)/fifo \
+ $(RTL_DIR)/mcu \
+ $(RTL_DIR)/memory \
+ $(RTL_DIR)/n64 \
+ $(RTL_DIR)/sd \
+ $(RTL_DIR)/serv \
+ $(RTL_DIR)/usb \
+ $(RTL_DIR)/vendor \
+ $(RTL_DIR) \
+ $(MOCKS_DIR)/vendor \
+ $(MOCKS_DIR)
+
+INC_DIRS = $(addprefix -I, $(SRC_DIRS))
+TEST_FILES = $(shell find "./$(BENCHES_DIR)" -not -path "$(BUILD_DIR)/*" -type f -name "*_tb.sv")
+TESTS = $(addprefix $(BUILD_DIR)/, $(basename $(TEST_FILES)))
+
+VERILATOR_FLAGS = --binary --trace --timescale 10ns/1ns -j --quiet $(INC_DIRS)
+
+$(BUILD_DIR)/%: %.sv
+ @echo "[VERILATOR] $<"
+ @mkdir -p $@.obj
+ @verilator $(VERILATOR_FLAGS) -Mdir $@.obj $< > /dev/null
+ @$@.obj/V$(notdir $@)
+
+tests: $(TESTS)
+
+clean:
+ @rm -rf ./$(BUILD_DIR)
+
+.PHONY: tests
diff --git a/fw/tests/benches/memory_dma_tb.sv b/fw/tests/benches/memory_dma_tb.sv
new file mode 100644
index 0000000..f07efc0
--- /dev/null
+++ b/fw/tests/benches/memory_dma_tb.sv
@@ -0,0 +1,126 @@
+module memory_dma_tb;
+
+ logic clk;
+ logic reset;
+
+ dma_scb dma_scb ();
+ fifo_bus fifo_bus ();
+ mem_bus mem_bus ();
+
+ logic start;
+ logic stop;
+ logic direction;
+ logic byte_swap;
+ logic [26:0] starting_address;
+ logic [26:0] transfer_length;
+
+ logic flush;
+ logic rx_fill_enabled;
+ logic tx_drain_enabled;
+
+ memory_dma memory_dma (
+ .clk(clk),
+ .reset(reset),
+
+ .dma_scb(dma_scb),
+ .fifo_bus(fifo_bus),
+ .mem_bus(mem_bus)
+ );
+
+ dma_controller_mock dma_controller_mock (
+ .clk(clk),
+ .reset(reset),
+
+ .dma_scb(dma_scb),
+
+ .start(start),
+ .stop(stop),
+ .direction(direction),
+ .byte_swap(byte_swap),
+ .starting_address(starting_address),
+ .transfer_length(transfer_length)
+ );
+
+ fifo_bus_fifo_mock #(
+ .DEPTH(8),
+ .FILL_RATE(3),
+ .DRAIN_RATE(3)
+ ) fifo_bus_fifo_mock (
+ .clk(clk),
+ .reset(reset),
+
+ .fifo_bus(fifo_bus),
+
+ .flush(flush),
+
+ .rx_fill_enabled(rx_fill_enabled),
+ .tx_drain_enabled(tx_drain_enabled)
+ );
+
+ memory_sdram_mock memory_sdram_mock (
+ .clk(clk),
+ .reset(reset),
+
+ .mem_bus(mem_bus)
+ );
+
+ initial begin
+ clk = 1'b0;
+ forever begin
+ clk = ~clk; #0.5;
+ end
+ end
+
+ initial begin
+ reset = 1'b0;
+ #10;
+ reset = 1'b1;
+ #10;
+ reset = 1'b0;
+ end
+
+ initial begin
+ $dumpfile("traces/memory_dma_tb.vcd");
+
+ #10000;
+
+ $dumpvars();
+
+ #100;
+ start = 1'b1;
+ direction = 1'b0;
+ byte_swap = 1'b0;
+ starting_address = 27'hFFF1;
+ transfer_length = 27'd64;
+ #1;
+ start = 1'b0;
+
+ #9;
+ tx_drain_enabled = 1'b1;
+
+ #490;
+ stop = 1'b1;
+ #1;
+ stop = 1'b0;
+
+ #165;
+
+ start = 1'b1;
+ direction = 1'b1;
+ #1;
+ start = 1'b0;
+
+ #9;
+ rx_fill_enabled = 1'b1;
+
+ #490;
+ stop = 1'b1;
+ #1;
+ stop = 1'b0;
+
+ #99;
+
+ $finish;
+ end
+
+endmodule
diff --git a/fw/tests/benches/usb_ft1248_tb.sv b/fw/tests/benches/usb_ft1248_tb.sv
new file mode 100644
index 0000000..8c62dd5
--- /dev/null
+++ b/fw/tests/benches/usb_ft1248_tb.sv
@@ -0,0 +1,119 @@
+module usb_ft1248_tb;
+
+ logic clk;
+ logic reset;
+
+ usb_scb usb_scb ();
+ fifo_bus fifo_bus ();
+
+ logic usb_pwrsav;
+ logic usb_clk;
+ logic usb_cs;
+ logic usb_miso;
+ logic [7:0] usb_miosi;
+
+ usb_ft1248 usb_ft1248 (
+ .clk(clk),
+ .reset(reset),
+ .usb_scb(usb_scb),
+ .fifo_bus(fifo_bus),
+ .usb_pwrsav(usb_pwrsav),
+ .usb_clk(usb_clk),
+ .usb_cs(usb_cs),
+ .usb_miso(usb_miso),
+ .usb_miosi(usb_miosi)
+ );
+
+ initial begin
+ clk = 1'b0;
+ forever begin
+ clk = ~clk; #0.5;
+ end
+ end
+
+ initial begin
+ reset = 1'b1;
+ #10;
+ reset = 1'b0;
+ end
+
+ initial begin
+ $dumpfile("traces/usb_ft1248_tb.vcd");
+
+ $dumpvars();
+
+ usb_pwrsav = 1'b1;
+ usb_miso = 1'b1;
+
+ #100;
+
+ fifo_bus.tx_write = 1'b1;
+ #100;
+ fifo_bus.tx_write = 1'b0;
+
+ #103;
+
+ usb_miso = 1'b0;
+ #80;
+ usb_scb.write_buffer_flush = 1'b1;
+ #1;
+ usb_scb.write_buffer_flush = 1'b0;
+ #20;
+ usb_miso = 1'b1;
+ #26;
+ usb_miso = 1'b0;
+
+ #4430;
+
+ usb_miso = 1'b1;
+ #13;
+ usb_miso = 1'b0;
+
+ #79;
+
+ fifo_bus.rx_read = 1'b1;
+ #1;
+ fifo_bus.rx_read = 1'b0;
+
+ #10;
+
+ fifo_bus.rx_read = 1'b1;
+ #1;
+ fifo_bus.rx_read = 1'b0;
+
+ #80;
+
+ fifo_bus.rx_read = 1'b1;
+ #1;
+ fifo_bus.rx_read = 1'b0;
+
+ #200;
+
+ usb_scb.reset_on_ack = 1'b1;
+ #1;
+ usb_scb.reset_on_ack = 1'b0;
+
+ #200;
+
+ usb_scb.reset_off_ack = 1'b1;
+ #1;
+ usb_scb.reset_off_ack = 1'b0;
+
+ #200;
+
+ usb_scb.fifo_flush = 1'b1;
+ #1;
+ usb_scb.fifo_flush = 1'b0;
+
+ #3000;
+
+ usb_scb.fifo_flush = 1'b1;
+ #1;
+ usb_scb.fifo_flush = 1'b0;
+
+ #6000;
+
+ $finish;
+ end
+
+endmodule
diff --git a/fw/tests/docker_run.sh b/fw/tests/docker_run.sh
new file mode 100755
index 0000000..908d27e
--- /dev/null
+++ b/fw/tests/docker_run.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+pushd $(dirname $0) > /dev/null
+
+docker run \
+ -it \
+ --rm \
+ --user $(id -u):$(id -g) \
+ -v "$(pwd)":/work \
+ -v "$(pwd)/../rtl":/rtl \
+ -e CCACHE_DIR=/tmp/ccache \
+ --entrypoint /bin/bash \
+ verilator/verilator:latest \
+ -c "make -j"
+
+BUILD_ERROR=$?
+
+popd > /dev/null
+
+if [ $BUILD_ERROR -ne 0 ]; then
+ exit -1
+fi
diff --git a/fw/tests/mocks/dma_controller_mock.sv b/fw/tests/mocks/dma_controller_mock.sv
new file mode 100644
index 0000000..d1d3b54
--- /dev/null
+++ b/fw/tests/mocks/dma_controller_mock.sv
@@ -0,0 +1,39 @@
+module dma_controller_mock (
+ input clk,
+ input reset,
+
+ dma_scb.controller dma_scb,
+
+ input start,
+ input stop,
+ input direction,
+ input byte_swap,
+ input [26:0] starting_address,
+ input [26:0] transfer_length
+);
+
+ always_ff @(posedge clk) begin
+ dma_scb.start <= 1'b0;
+ dma_scb.stop <= 1'b0;
+
+ if (reset) begin
+ dma_scb.direction <= 1'b0;
+ dma_scb.byte_swap <= 1'b0;
+ dma_scb.starting_address <= 27'd0;
+ dma_scb.transfer_length <= 27'd0;
+ end else begin
+ if (start) begin
+ dma_scb.start <= 1'b1;
+ dma_scb.direction <= direction;
+ dma_scb.byte_swap <= byte_swap;
+ dma_scb.starting_address <= starting_address;
+ dma_scb.transfer_length <= transfer_length;
+ end
+
+ if (stop) begin
+ dma_scb.stop <= 1'b1;
+ end
+ end
+ end
+
+endmodule
diff --git a/fw/tests/mocks/fifo_bus_fifo_mock.sv b/fw/tests/mocks/fifo_bus_fifo_mock.sv
new file mode 100644
index 0000000..2fc57de
--- /dev/null
+++ b/fw/tests/mocks/fifo_bus_fifo_mock.sv
@@ -0,0 +1,145 @@
+module fifo_bus_fifo_mock #(
+ parameter int DEPTH = 1024,
+ parameter int FILL_RATE = 3,
+ parameter int DRAIN_RATE = 3
+) (
+ input clk,
+ input reset,
+
+ fifo_bus.fifo fifo_bus,
+
+ input flush,
+
+ input rx_fill_enabled,
+ input tx_drain_enabled
+);
+
+ localparam int PTR_BITS = $clog2(DEPTH);
+
+
+ // RX FIFO mock
+
+ logic rx_full;
+ logic rx_write;
+ logic [7:0] rx_wdata;
+
+ logic [PTR_BITS:0] rx_count;
+
+ fifo_mock #(
+ .DEPTH(DEPTH)
+ ) fifo_rx (
+ .clk(clk),
+ .reset(reset),
+
+ .empty(fifo_bus.rx_empty),
+ .read(fifo_bus.rx_read),
+ .rdata(fifo_bus.rx_rdata),
+
+ .full(rx_full),
+ .write(rx_write),
+ .wdata(rx_wdata),
+
+ .count(rx_count)
+ );
+
+ localparam int FILL_BITS = $clog2(FILL_RATE);
+ logic [FILL_BITS:0] fill_counter;
+ logic rx_fill;
+
+ always_ff @(posedge clk) begin
+ rx_fill <= rx_fill_enabled;
+ end
+
+ generate;
+ if (FILL_RATE == 0) begin
+ always_comb begin
+ rx_write = rx_fill && !rx_full;
+ end
+ end else begin
+ always_comb begin
+ rx_write = rx_fill && !rx_full && (fill_counter == (FILL_BITS + 1)'(FILL_RATE));
+ end
+ always_ff @(posedge clk) begin
+ if (fill_counter < (FILL_BITS + 1)'(FILL_RATE)) begin
+ fill_counter <= fill_counter + (FILL_BITS + 1)'('d1);
+ end
+ if (reset) begin
+ fill_counter <= (FILL_BITS + 1)'('d0);
+ end else begin
+ if (rx_write) begin
+ fill_counter <= (FILL_BITS + 1)'('d0);
+ end
+ end
+ end
+ end
+ endgenerate
+
+ always_ff @(posedge clk) begin
+ if (reset) begin
+ rx_wdata <= 8'h01;
+ end else begin
+ if (rx_write) begin
+ rx_wdata <= rx_wdata + 8'h01;
+ end
+ end
+ end
+
+
+ // TX FIFO mock
+
+ logic tx_empty;
+ logic tx_read;
+ logic [7:0] tx_rdata;
+
+ logic [PTR_BITS:0] tx_count;
+
+ fifo_mock #(
+ .DEPTH(DEPTH)
+ ) fifo_tx (
+ .clk(clk),
+ .reset(reset),
+
+ .empty(tx_empty),
+ .read(tx_read),
+ .rdata(tx_rdata),
+
+ .full(fifo_bus.tx_full),
+ .write(fifo_bus.tx_write),
+ .wdata(fifo_bus.tx_wdata),
+
+ .count(tx_count)
+ );
+
+ localparam int DRAIN_BITS = $clog2(DRAIN_RATE);
+ logic [DRAIN_BITS:0] drain_counter;
+ logic tx_drain;
+
+ always_ff @(posedge clk) begin
+ tx_drain <= tx_drain_enabled;
+ end
+
+ generate;
+ if (DRAIN_RATE == 0) begin
+ always_comb begin
+ tx_read = tx_drain && !tx_empty;
+ end
+ end else begin
+ always_comb begin
+ tx_read = tx_drain && !tx_empty && (drain_counter == (DRAIN_BITS + 1)'(DRAIN_RATE));
+ end
+ always_ff @(posedge clk) begin
+ if (drain_counter < (DRAIN_BITS + 1)'(DRAIN_RATE)) begin
+ drain_counter <= drain_counter + (DRAIN_BITS + 1)'('d1);
+ end
+ if (reset) begin
+ drain_counter <= (DRAIN_BITS + 1)'('d0);
+ end else begin
+ if (tx_read) begin
+ drain_counter <= (DRAIN_BITS + 1)'('d0);
+ end
+ end
+ end
+ end
+ endgenerate
+
+endmodule
diff --git a/fw/tests/mocks/fifo_mock.sv b/fw/tests/mocks/fifo_mock.sv
new file mode 100644
index 0000000..acf9065
--- /dev/null
+++ b/fw/tests/mocks/fifo_mock.sv
@@ -0,0 +1,49 @@
+module fifo_mock #(
+ parameter int DEPTH = 1024,
+ localparam int PTR_BITS = $clog2(DEPTH)
+) (
+ input clk,
+ input reset,
+
+ output logic empty,
+ input read,
+ output [7:0] rdata,
+
+ output logic full,
+ input write,
+ input [7:0] wdata,
+
+ output logic [PTR_BITS:0] count
+);
+
+ logic [7:0] fifo_mem [0:(DEPTH - 1)];
+ logic [(PTR_BITS - 1):0] fifo_rptr;
+ logic [(PTR_BITS - 1):0] fifo_wptr;
+
+ always_comb begin
+ full = count >= (PTR_BITS + 1)'(DEPTH);
+ empty = count == (PTR_BITS + 1)'('d0);
+ end
+
+ always_ff @(posedge clk) begin
+ if (read) begin
+ rdata <= fifo_mem[fifo_rptr];
+ fifo_rptr <= fifo_rptr + PTR_BITS'('d1);
+ count <= count - (PTR_BITS + 1)'('d1);
+ end
+ if (write) begin
+ fifo_mem[fifo_wptr] <= wdata;
+ fifo_wptr <= fifo_wptr + PTR_BITS'('d1);
+ count <= count + (PTR_BITS + 1)'('d1);
+ end
+ if (read && write) begin
+ count <= count;
+ end
+ if (reset) begin
+ count <= (PTR_BITS + 1)'('d0);
+ fifo_rptr <= PTR_BITS'('d0);
+ fifo_wptr <= PTR_BITS'('d0);
+ end
+ end
+
+endmodule
diff --git a/fw/tests/mocks/memory_sdram_mock.sv b/fw/tests/mocks/memory_sdram_mock.sv
new file mode 100644
index 0000000..9f5d34a
--- /dev/null
+++ b/fw/tests/mocks/memory_sdram_mock.sv
@@ -0,0 +1,70 @@
+module memory_sdram_mock (
+ input clk,
+ input reset,
+
+ mem_bus.memory mem_bus
+);
+
+ logic sdram_cs;
+ logic sdram_ras;
+ logic sdram_cas;
+ logic sdram_we;
+ logic [1:0] sdram_ba;
+ logic [12:0] sdram_a;
+ logic [1:0] sdram_dqm;
+ logic [15:0] sdram_dq;
+
+ memory_sdram memory_sdram_inst (
+ .clk(clk),
+ .reset(reset),
+
+ .mem_bus(mem_bus),
+
+ .sdram_cs(sdram_cs),
+ .sdram_ras(sdram_ras),
+ .sdram_cas(sdram_cas),
+ .sdram_we(sdram_we),
+ .sdram_ba(sdram_ba),
+ .sdram_a(sdram_a),
+ .sdram_dqm(sdram_dqm),
+ .sdram_dq(sdram_dq)
+ );
+
+ logic [1:0] cas_delay;
+ logic [15:0] data_from_sdram;
+ logic [15:0] data_to_sdram;
+ logic [15:0] sdram_dq_driven;
+
+ assign sdram_dq = sdram_dq_driven;
+
+ always_ff @(posedge clk) begin
+ if (reset) begin
+ cas_delay <= 2'b00;
+ data_from_sdram <= 16'h0102;
+ data_to_sdram <= 16'hFFFF;
+ end else begin
+ cas_delay <= {cas_delay[0], 1'b0};
+
+ if ({sdram_cs, sdram_ras, sdram_cas, sdram_we} == 4'b0101) begin
+ cas_delay[0] <= 1'b1;
+ end
+
+ if (cas_delay[1]) begin
+ data_from_sdram <= data_from_sdram + 16'h0202;
+ end
+
+ if ({sdram_cs, sdram_ras, sdram_cas, sdram_we} == 4'b0100) begin
+ if (!sdram_dqm[0]) data_to_sdram[7:0] <= sdram_dq[7:0];
+ if (!sdram_dqm[1]) data_to_sdram[15:8] <= sdram_dq[15:8];
+ end
+ end
+ end
+
+ always_comb begin
+ sdram_dq_driven = 16'hXXXX;
+ if (cas_delay[1]) begin
+ sdram_dq_driven = data_from_sdram;
+ end
+ end
+
+endmodule
diff --git a/fw/tests/mocks/vendor/fifo_8kb.sv b/fw/tests/mocks/vendor/fifo_8kb.sv
new file mode 100644
index 0000000..f20105b
--- /dev/null
+++ b/fw/tests/mocks/vendor/fifo_8kb.sv
@@ -0,0 +1,33 @@
+module fifo_8kb (
+ input clk,
+ input reset,
+
+ output empty,
+ input read,
+ output [7:0] rdata,
+
+ output full,
+ input write,
+ input [7:0] wdata,
+
+ output logic [10:0] count
+);
+
+ fifo_mock #(
+ .DEPTH(1024)
+ ) fifo_8kb (
+ .clk(clk),
+ .reset(reset),
+
+ .empty(empty),
+ .read(read),
+ .rdata(rdata),
+
+ .full(full),
+ .write(write),
+ .wdata(wdata),
+
+ .count(count)
+ );
+
+endmodule
diff --git a/fw/tests/traces/.gitignore b/fw/tests/traces/.gitignore
new file mode 100644
index 0000000..6167be1
--- /dev/null
+++ b/fw/tests/traces/.gitignore
@@ -0,0 +1,2 @@
+*.vcd
+*.gtkw
diff --git a/sw/controller/src/usb.c b/sw/controller/src/usb.c
index 3287b98..212c0d2 100644
--- a/sw/controller/src/usb.c
+++ b/sw/controller/src/usb.c
@@ -201,6 +201,9 @@ static bool usb_dma_ready (void) {
}
static bool usb_validate_address_length (uint32_t address, uint32_t length, bool exclude_bootloader) {
+ if (length == 0) {
+ return true;
+ }
if ((address >= MEMORY_LENGTH) || (length > MEMORY_LENGTH)) {
return true;
}
diff --git a/sw/deployer/src/sc64/ftdi.rs b/sw/deployer/src/sc64/ftdi.rs
index f99c728..38574de 100644
--- a/sw/deployer/src/sc64/ftdi.rs
+++ b/sw/deployer/src/sc64/ftdi.rs
@@ -486,7 +486,7 @@ impl FtdiDevice {
wrapper.set_module_detach_mode(ModuleDetachMode::AutoDetachReattach);
wrapper.set_interface(InterfaceIndex::A)?;
- const CHUNK_SIZE: usize = 16 * 1024;
+ const CHUNK_SIZE: usize = 2 * 1024 * 1024;
wrapper.read_data_set_chunksize(CHUNK_SIZE)?;
wrapper.write_data_set_chunksize(CHUNK_SIZE)?;