From 24f04ae9169847d4b229fb1cbf6081b7774e8329 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Sun, 7 Jul 2024 00:09:03 +0200 Subject: [PATCH] verilator init --- fw/project/lcmxo2/sc64.ldf | 3 + fw/project/lcmxo2/sc64.lpf | 2 +- fw/rtl/memory/dma_scb.sv | 31 ++++ fw/rtl/memory/memory_dma.sv | 257 ++++++++++++++++++++++---- fw/rtl/memory/memory_sdram.sv | 123 ++++++------ fw/tests/.gitignore | 2 + fw/tests/Makefile | 33 ++++ fw/tests/memory/memory_dma_tb.sv | 126 +++++++++++++ fw/tests/mocks/dma_controller_mock.sv | 39 ++++ fw/tests/mocks/fifo_bus_mock.sv | 172 +++++++++++++++++ fw/tests/mocks/memory_sdram_mock.sv | 70 +++++++ fw/tests/traces/.gitkeep | 0 sw/controller/src/usb.c | 3 + 13 files changed, 772 insertions(+), 89 deletions(-) create mode 100644 fw/rtl/memory/dma_scb.sv create mode 100644 fw/tests/.gitignore create mode 100644 fw/tests/Makefile create mode 100644 fw/tests/memory/memory_dma_tb.sv create mode 100644 fw/tests/mocks/dma_controller_mock.sv create mode 100644 fw/tests/mocks/fifo_bus_mock.sv create mode 100644 fw/tests/mocks/memory_sdram_mock.sv create mode 100644 fw/tests/traces/.gitkeep 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; }