diff --git a/fw/project/lcmxo2/sc64.ldf b/fw/project/lcmxo2/sc64.ldf
index f3198e8..20dcae1 100644
--- a/fw/project/lcmxo2/sc64.ldf
+++ b/fw/project/lcmxo2/sc64.ldf
@@ -3,6 +3,9 @@
+
diff --git a/fw/project/lcmxo2/sc64.lpf b/fw/project/lcmxo2/sc64.lpf
index 6feb8ab..2d61e26 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 PAR_ADJ 20.000000 ;
BLOCK PATH TO PORT "mcu_int" ;
BLOCK PATH TO PORT "n64_irq" ;
BLOCK PATH FROM PORT "usb_pwrsav" ;
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..6c17173 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,6 +8,228 @@ module memory_dma (
mem_bus.controller mem_bus
);
+ // logic stop_requested;
+ // logic dma_done;
+
+ // always_ff @(posedge clk) begin
+ // if (reset) begin
+ // dma_scb.busy <= 1'b0;
+ // stop_requested <= 1'b0;
+ // end else begin
+ // if (dma_scb.start) begin
+ // dma_scb.busy <= 1'b1;
+ // end
+
+ // if (dma_scb.busy && dma_scb.stop) begin
+ // stop_requested <= 1'b1;
+ // end
+
+ // if (dma_done) begin
+ // dma_scb.busy <= 1'b0;
+ // stop_requested <= 1'b0;
+ // end
+ // end
+ // end
+
+ // logic unaligned_start;
+ // logic unaligned_end;
+
+ // always_ff @(posedge clk) begin
+ // if (dma_scb.start) begin
+ // unaligned_start <= dma_scb.starting_address[0];
+ // // unaligned_end <= ;
+ // end
+ // end
+
+ // logic [26:0] bytes_remaining;
+
+ // always_ff @(posedge clk) begin
+ // bytes_remaining <= bytes_remaining - 27'd1;
+ // if (dma_scb.start) begin
+ // bytes_remaining <= dma_scb.transfer_length;
+ // end
+ // end
+
+
+
+
+ // logic mem_transfer_request;
+ // logic mem_wdata_buffer_ready;
+ // logic mem_rdata_buffer_ready;
+ // logic [15:0] mem_wdata_buffer;
+ // logic [15:0] mem_rdata_buffer;
+ // logic [1:0] mem_wdata_buffer_valid_bytes;
+ // logic mem_rdata_buffer_valid;
+
+ // always_comb begin
+ // mem_transfer_request = (
+ // !mem_bus.request || mem_bus.ack
+ // ) && (
+ // mem_wdata_buffer_ready || mem_rdata_buffer_ready
+ // );
+ // end
+
+ // always_ff @(posedge clk) begin
+ // if (dma_scb.start) begin
+ // mem_bus.write <= dma_scb.direction;
+ // mem_bus.address <= {dma_scb.starting_address[26:1], 1'b0};
+ // mem_rdata_buffer_valid <= 1'b0;
+ // end
+
+ // if (mem_bus.ack) begin
+ // mem_bus.request <= 1'b0;
+ // mem_bus.address <= mem_bus.address + 27'd2;
+ // mem_rdata_buffer <= mem_bus.rdata;
+ // mem_rdata_buffer_valid <= 1'b1;
+ // end
+
+ // if (reset) begin
+ // mem_bus.request <= 1'b0;
+ // end else if (mem_transfer_request) begin
+ // mem_bus.request <= 1'b1;
+ // mem_bus.wmask <= mem_wdata_buffer_valid_bytes;
+ // end
+
+ // if (!mem_bus.request || mem_bus.ack) begin
+ // if (mem_wdata_buffer_ready) begin
+ // if (dma_scb.byte_swap) begin
+ // mem_bus.wdata[15:8] <= mem_wdata_buffer[7:0];
+ // mem_bus.wdata[7:0] <= mem_wdata_buffer[15:8];
+ // end else begin
+ // mem_bus.wdata <= mem_wdata_buffer;
+ // end
+ // end
+ // end
+ // end
+
+
+
+
+
+
+
+
+ // // always_ff @(posedge clk) begin
+ // // mem_wdata_buffer_ready <= dma_scb.busy;
+ // // mem_wdata_buffer_valid_bytes <= 2'b10;
+ // // end
+
+
+
+
+
+
+ // logic [1:0] rx_fifo_bytes_available;
+ // logic [1:0] tx_fifo_bytes_available;
+
+ // always_comb begin
+ // rx_fifo_bytes_available = 2'd2;
+ // if (fifo_bus.rx_almost_empty) begin
+ // rx_fifo_bytes_available = 2'd1;
+ // end
+ // if (fifo_bus.rx_empty) begin
+ // rx_fifo_bytes_available = 2'd0;
+ // end
+
+ // tx_fifo_bytes_available = 2'd2;
+ // if (fifo_bus.tx_almost_full) begin
+ // tx_fifo_bytes_available = 2'd1;
+ // end
+ // if (fifo_bus.tx_full) begin
+ // tx_fifo_bytes_available = 2'd0;
+ // end
+ // end
+
+ // always_ff @(posedge clk) begin
+ // if (dma_scb.busy) begin
+ // if (!dma_scb.direction) begin
+ // // RX FIFO handling
+ // end
+ // end
+ // end
+
+ // always_ff @(posedge clk) begin
+ // if (dma_scb.busy) begin
+ // if (dma_scb.direction) begin
+ // // TX FIFO handling
+ // end
+ // end
+ // end
+
+
+
+//XDDDAWDWD
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ // always_ff @(posedge clk) begin
+ // dma_done <= 1'b0;
+
+ // if (dma_scb.busy && bytes_remaining == 27'd0) begin
+ // // dma_done <= 1'b1;
+ // end
+ // end
+
+
+
+
+
+ // typedef enum bit [1:0] {
+
+ // } e_rx_fifo_state;
+
+ // typedef enum bit [1:0] {
+
+ // } e_tx_fifo_state;
+
+ // typedef enum bit [1:0] {
+ // MEM_BUS_IDLE = 2'b00,
+ // MEM_BUS_WAIT = 2'b01,
+ // MEM_BUS_TRANSFERRING = 2'b10,
+ // } e_mem_bus_state;
+
+ // e_mem_bus_state mem_bus_state;
+ // e_mem_bus_state next_mem_bus_state;
+
+ // always_ff @(posedge clk) begin
+ // mem_bus_state <= next_mem_bus_state;
+ // if (reset) begin
+ // mem_bus_state <= MEM_BUS_IDLE;
+ // end
+ // end
+
+ // always_comb begin
+ // next_mem_bus_state = mem_bus_state;
+
+ // case (mem_bus_state)
+ // MEM_BUS_IDLE: begin
+ // if (dma_scb.start) begin
+ // next_mem_bus_state = MEM_BUS_WAIT;
+ // end
+ // end
+
+ // MEM_BUS_WAIT: begin
+ // i
+ // end
+
+ // MEM_BUS_TRANSFERRING: begin
+
+ // end
+ // endcase
+ // end
+
+
// DMA start/stop control
logic dma_start;
@@ -260,7 +449,7 @@ module memory_dma (
end
if (mem_bus.ack) begin
- mem_bus.address <= mem_bus.address + 2'd2;
+ mem_bus.address <= mem_bus.address + 27'd2;
end
end
diff --git a/fw/rtl/memory/memory_sdram.sv b/fw/rtl/memory/memory_sdram.sv
index fce983a..cbd0ffd 100644
--- a/fw/rtl/memory/memory_sdram.sv
+++ b/fw/rtl/memory/memory_sdram.sv
@@ -14,29 +14,36 @@ 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_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_812.5;
- 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_RC = 5'(int'($ceil(T_RC / T_CLK)));
+ const bit [4:0] C_RP = 5'(int'($ceil(T_RP / T_CLK)));
+ const bit [4:0] C_RCD = 5'(int'($ceil(T_RCD / T_CLK)));
+ const bit [4:0] C_MRD = 5'(int'($ceil(T_MRD / T_CLK)));
+ const bit [13:0] C_REF = 14'(int'($ceil(T_REF / 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 +65,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 +80,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'b00, // [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 +137,38 @@ 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 powerup_done;
logic pending_refresh;
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
end
@@ -157,6 +176,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,7 +222,7 @@ 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
@@ -229,18 +249,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/tests/.gitignore b/fw/tests/.gitignore
new file mode 100644
index 0000000..4fc31cd
--- /dev/null
+++ b/fw/tests/.gitignore
@@ -0,0 +1,2 @@
+/build
+*.vcd
diff --git a/fw/tests/Makefile b/fw/tests/Makefile
new file mode 100644
index 0000000..0c17476
--- /dev/null
+++ b/fw/tests/Makefile
@@ -0,0 +1,33 @@
+RTL_DIR = ../rtl
+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)
+
+INC_DIRS = $(addprefix -I, $(SRC_DIRS))
+TEST_FILES = $(shell find "./" -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/memory/memory_dma_tb.sv b/fw/tests/memory/memory_dma_tb.sv
new file mode 100644
index 0000000..359c54b
--- /dev/null
+++ b/fw/tests/memory/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_mock #(
+ .DEPTH(16),
+ .FILL_RATE(16),
+ .DRAIN_RATE(16)
+ ) fifo_bus_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'hFFF0;
+ transfer_length = 27'd32;
+ #1;
+ start = 1'b0;
+
+ #9;
+ tx_drain_enabled = 1'b1;
+
+ #490;
+ stop = 1'b1;
+ #1;
+ stop = 1'b0;
+
+ #99;
+
+ 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/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_mock.sv b/fw/tests/mocks/fifo_bus_mock.sv
new file mode 100644
index 0000000..17828de
--- /dev/null
+++ b/fw/tests/mocks/fifo_bus_mock.sv
@@ -0,0 +1,172 @@
+module fifo_bus_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 [7:0] rx_fifo_mem [0:(DEPTH - 1)];
+ logic [(PTR_BITS - 1):0] rx_fifo_rptr;
+ logic [(PTR_BITS - 1):0] rx_fifo_wptr;
+ logic [PTR_BITS:0] rx_available;
+ logic rx_fifo_full;
+ logic rx_fifo_almost_full;
+ logic rx_write;
+ logic [7:0] rx_wdata;
+
+ always_comb begin
+ rx_fifo_full = rx_available >= (PTR_BITS + 1)'(DEPTH);
+ rx_fifo_almost_full = rx_available >= (PTR_BITS + 1)'(DEPTH - 1);
+ fifo_bus.rx_empty = rx_available == (PTR_BITS + 1)'('d0);
+ fifo_bus.rx_almost_empty = rx_available <= (PTR_BITS + 1)'('d1);
+ end
+
+ always_ff @(posedge clk) begin
+ if (fifo_bus.rx_read) begin
+ fifo_bus.rx_rdata <= rx_fifo_mem[rx_fifo_rptr];
+ rx_fifo_rptr <= rx_fifo_rptr + PTR_BITS'('d1);
+ rx_available <= rx_available - (PTR_BITS + 1)'('d1);
+ end
+ if (rx_write) begin
+ rx_fifo_mem[rx_fifo_wptr] <= rx_wdata;
+ rx_fifo_wptr <= rx_fifo_wptr + PTR_BITS'('d1);
+ rx_available <= rx_available + (PTR_BITS + 1)'('d1);
+ end
+ if (fifo_bus.rx_read && rx_write) begin
+ rx_available <= rx_available;
+ end
+ if (reset || flush) begin
+ rx_available <= (PTR_BITS + 1)'('d0);
+ rx_fifo_rptr <= PTR_BITS'('d0);
+ rx_fifo_wptr <= PTR_BITS'('d0);
+ end
+ end
+
+ 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_fifo_full;
+ end
+ end else begin
+ always_comb begin
+ rx_write = rx_fill && !rx_fifo_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 [7:0] tx_fifo_mem [0:(DEPTH - 1)];
+ logic [(PTR_BITS - 1):0] tx_fifo_rptr;
+ logic [(PTR_BITS - 1):0] tx_fifo_wptr;
+ logic [PTR_BITS:0] tx_available;
+ logic tx_fifo_empty;
+ logic tx_fifo_almost_empty;
+ logic tx_read;
+ logic [7:0] tx_rdata;
+
+ always_comb begin
+ tx_fifo_empty = tx_available == (PTR_BITS + 1)'('d0);
+ tx_fifo_almost_empty = tx_available <= (PTR_BITS + 1)'('d1);
+ fifo_bus.tx_full = tx_available >= (PTR_BITS + 1)'(DEPTH);
+ fifo_bus.tx_almost_full = tx_available >= (PTR_BITS + 1)'(DEPTH - 1);
+ end
+
+ always_ff @(posedge clk) begin
+ if (fifo_bus.tx_write) begin
+ tx_fifo_mem[tx_fifo_wptr] <= fifo_bus.tx_wdata;
+ tx_fifo_wptr <= tx_fifo_wptr + PTR_BITS'('d1);
+ tx_available <= tx_available + (PTR_BITS + 1)'('d1);
+ end
+ if (tx_read) begin
+ tx_rdata <= tx_fifo_mem[tx_fifo_rptr];
+ tx_fifo_rptr <= tx_fifo_rptr + PTR_BITS'('d1);
+ tx_available <= tx_available - (PTR_BITS + 1)'('d1);
+ end
+ if (fifo_bus.tx_write && tx_read) begin
+ tx_available <= tx_available;
+ end
+ if (reset || flush) begin
+ tx_available <= (PTR_BITS + 1)'('d0);
+ tx_fifo_rptr <= PTR_BITS'('d0);
+ tx_fifo_wptr <= PTR_BITS'('d0);
+ end
+ end
+
+ 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_fifo_empty;
+ end
+ end else begin
+ always_comb begin
+ tx_read = tx_drain && !tx_fifo_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/memory_sdram_mock.sv b/fw/tests/mocks/memory_sdram_mock.sv
new file mode 100644
index 0000000..f24a61d
--- /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;
+
+ initial begin
+ cas_delay = 2'b00;
+ data_from_sdram = 16'h0102;
+ data_to_sdram = 16'hFFFF;
+ end
+
+ always_ff @(posedge clk) 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
+
+ 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/traces/.gitkeep b/fw/tests/traces/.gitkeep
new file mode 100644
index 0000000..e69de29
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;
}