From 8f157af3d6fe53396f2f80068523846034816939 Mon Sep 17 00:00:00 2001 From: Sebastian Holzapfel Date: Tue, 19 Sep 2023 20:00:22 +0200 Subject: [PATCH] Add system level integration test --- gateware/boards/icebreaker/sysmgr.v | 8 ++ gateware/cal/cal.sv | 136 ++++++++++-------- gateware/drivers/ak4619.sv | 7 - gateware/drivers/pmod_i2c_master.sv | 15 +- gateware/eurorack_pmod.sv | 10 +- gateware/sim/integration/Makefile | 18 +++ gateware/sim/integration/cal/cal_mem.hex | 1 + .../sim/integration/drivers/ak4619-cfg.hex | 1 + .../sim/integration/drivers/pca9635-cfg.hex | 1 + gateware/sim/integration/tb_integration.py | 62 ++++++++ gateware/top.sv | 9 ++ 11 files changed, 189 insertions(+), 79 deletions(-) create mode 100644 gateware/sim/integration/Makefile create mode 120000 gateware/sim/integration/cal/cal_mem.hex create mode 120000 gateware/sim/integration/drivers/ak4619-cfg.hex create mode 120000 gateware/sim/integration/drivers/pca9635-cfg.hex create mode 100644 gateware/sim/integration/tb_integration.py diff --git a/gateware/boards/icebreaker/sysmgr.v b/gateware/boards/icebreaker/sysmgr.v index e14ff27..70dd906 100644 --- a/gateware/boards/icebreaker/sysmgr.v +++ b/gateware/boards/icebreaker/sysmgr.v @@ -30,6 +30,7 @@ module sysmgr ( assign clk_fs = clkdiv[7]; // PLL instance +`ifndef COCOTB_SIM `ifndef VERILATOR_LINT_ONLY SB_PLL40_2F_PAD #( .DIVR(4'b0000), @@ -57,6 +58,9 @@ module sysmgr ( .SCLK(1'b0) ); `endif +`else + assign clk_1x_i = clk_in; +`endif // Logic reset generation always @(posedge clk_1x_i or negedge pll_lock) @@ -72,11 +76,15 @@ module sysmgr ( clkdiv <= clkdiv + 1; +`ifndef COCOTB_SIM `ifndef VERILATOR_LINT_ONLY SB_GB rst_gbuf_I ( .USER_SIGNAL_TO_GLOBAL_BUFFER(rst_i), .GLOBAL_BUFFER_OUTPUT(rst_out) ); `endif +`else + assign rst_out = rst_i; +`endif endmodule // sysmgr diff --git a/gateware/cal/cal.sv b/gateware/cal/cal.sv index f2d9742..c2b4f25 100644 --- a/gateware/cal/cal.sv +++ b/gateware/cal/cal.sv @@ -20,6 +20,7 @@ module cal #( parameter W = 16, // sample width parameter CAL_MEM_FILE = "cal/cal_mem.hex" )( + input rst, input clk_256fs, input clk_fs, input [7:0] jack, @@ -57,9 +58,9 @@ localparam int signed CLAMPH = 32'sd32000; logic signed [W-1:0] cal_mem [0:(2*N_CHANNELS)-1]; logic signed [(2*W)-1:0] out [N_CHANNELS]; -logic [2:0] ch = 0; -logic [2:0] state = CAL_ST_ZERO; -logic l_clk_fs = 1'd0; +logic [2:0] ch; +logic [2:0] state; +logic l_clk_fs; // Calibration memory for 8 channels stored as // 2 bytes shift, 2 bytes multiply * 8 channels. @@ -67,70 +68,87 @@ initial $readmemh(CAL_MEM_FILE, cal_mem); always_ff @(posedge clk_256fs) begin - // On rising clk_fs. - if (clk_fs && (l_clk_fs != clk_fs)) begin - state <= CAL_ST_LATCH; + if (rst) begin + l_clk_fs <= 0; ch <= 0; + state <= CAL_ST_LATCH; + out[0] <= 0; + out[1] <= 0; + out[2] <= 0; + out[3] <= 0; + out[4] <= 0; + out[5] <= 0; + out[6] <= 0; + out[7] <= 0; end else begin - ch <= ch + 1; - end - case (state) - CAL_ST_LATCH: begin - case (ch) - 0: out[0] <= 32'(in0); - 1: out[1] <= 32'(in1); - 2: out[2] <= 32'(in2); - 3: out[3] <= 32'(in3); - 4: out[4] <= 32'(in4); - 5: out[5] <= 32'(in5); - 6: out[6] <= 32'(in6); - 7: out[7] <= 32'(in7); - endcase - if (ch == LAST_CH_IX) state <= CAL_ST_ZERO; - end - CAL_ST_ZERO: begin - out[ch] <= (out[ch] - 32'(cal_mem[{ch, 1'b0}])); - if (ch == LAST_CH_IX) state <= CAL_ST_MULTIPLY; - end - CAL_ST_MULTIPLY: begin - out[ch] <= (out[ch] * cal_mem[{ch, 1'b1}]) >>> 10; - if (ch == LAST_CH_IX) state <= CAL_ST_CLAMPL; - end - CAL_ST_CLAMPL: begin - out[ch] <= ((out[ch] < CLAMPL) ? CLAMPL : out[ch]); - if (ch == LAST_CH_IX) state <= CAL_ST_CLAMPH; - end - CAL_ST_CLAMPH: begin - out[ch] <= ((out[ch] > CLAMPH) ? CLAMPH : out[ch]); - if (ch == LAST_CH_IX) state <= CAL_ST_OUT; - end - CAL_ST_OUT: begin - // Calibrated input samples are zeroed if jack disconnected. - out0 <= jack[0] ? out[0][W-1:0] : 0; - out1 <= jack[1] ? out[1][W-1:0] : 0; - out2 <= jack[2] ? out[2][W-1:0] : 0; - out3 <= jack[3] ? out[3][W-1:0] : 0; - out4 <= out[4][W-1:0]; - out5 <= out[5][W-1:0]; - out6 <= out[6][W-1:0]; - out7 <= out[7][W-1:0]; - state <= CAL_ST_HALT; - end - default: begin - // Halt and do nothing. + l_clk_fs <= clk_fs; + + // On rising clk_fs. + if (clk_fs && (l_clk_fs != clk_fs)) begin + state <= CAL_ST_LATCH; + ch <= 0; + end else begin + ch <= ch + 1; end - endcase - l_clk_fs <= clk_fs; + case (state) + CAL_ST_LATCH: begin + case (ch) + 0: out[0] <= 32'(in0); + 1: out[1] <= 32'(in1); + 2: out[2] <= 32'(in2); + 3: out[3] <= 32'(in3); + 4: out[4] <= 32'(in4); + 5: out[5] <= 32'(in5); + 6: out[6] <= 32'(in6); + 7: out[7] <= 32'(in7); + endcase + if (ch == LAST_CH_IX) state <= CAL_ST_ZERO; + end + CAL_ST_ZERO: begin + out[ch] <= (out[ch] - 32'(cal_mem[{ch, 1'b0}])); + if (ch == LAST_CH_IX) state <= CAL_ST_MULTIPLY; + end + CAL_ST_MULTIPLY: begin + out[ch] <= (out[ch] * cal_mem[{ch, 1'b1}]) >>> 10; + if (ch == LAST_CH_IX) state <= CAL_ST_CLAMPL; + end + CAL_ST_CLAMPL: begin + out[ch] <= ((out[ch] < CLAMPL) ? CLAMPL : out[ch]); + if (ch == LAST_CH_IX) state <= CAL_ST_CLAMPH; + end + CAL_ST_CLAMPH: begin + out[ch] <= ((out[ch] > CLAMPH) ? CLAMPH : out[ch]); + if (ch == LAST_CH_IX) state <= CAL_ST_OUT; + end + CAL_ST_OUT: begin + // Calibrated input samples are zeroed if jack disconnected. + out0 <= out[0][W-1:0]; + out1 <= out[1][W-1:0]; + out2 <= out[2][W-1:0]; + out3 <= out[3][W-1:0]; + out4 <= out[4][W-1:0]; + out5 <= out[5][W-1:0]; + out6 <= out[6][W-1:0]; + out7 <= out[7][W-1:0]; + state <= CAL_ST_HALT; + end + default: begin + // Halt and do nothing. + end + endcase + end end `ifdef COCOTB_SIM -initial begin - $dumpfile ("cal.vcd"); - $dumpvars; - #1; -end +generate + genvar idx; + for(idx = 0; idx < 8; idx = idx+1) begin: register + wire [31:0] tmp; + assign tmp = out[idx]; + end +endgenerate `endif endmodule diff --git a/gateware/drivers/ak4619.sv b/gateware/drivers/ak4619.sv index f096386..5883ca3 100644 --- a/gateware/drivers/ak4619.sv +++ b/gateware/drivers/ak4619.sv @@ -122,12 +122,5 @@ always_ff @(posedge clk_256fs) begin end end -`ifdef COCOTB_SIM -initial begin - $dumpfile ("ak4619.vcd"); - $dumpvars; - #1; -end -`endif endmodule diff --git a/gateware/drivers/pmod_i2c_master.sv b/gateware/drivers/pmod_i2c_master.sv index 7e3915b..2d3024a 100644 --- a/gateware/drivers/pmod_i2c_master.sv +++ b/gateware/drivers/pmod_i2c_master.sv @@ -63,6 +63,11 @@ localparam I2C_DELAY1 = 0, I2C_JACK2 = 8, // >>--/ I2C_IDLE = 9; +`ifdef COCOTB_SIM +localparam STARTUP_DELAY_BIT = 4; +`else +localparam STARTUP_DELAY_BIT = 17; +`endif logic [3:0] i2c_state = I2C_DELAY1; @@ -109,7 +114,7 @@ always_ff @(posedge clk) begin if (ready && ~stb) begin case (i2c_state) I2C_DELAY1: begin - if(delay_cnt[17]) + if(delay_cnt[STARTUP_DELAY_BIT]) i2c_state <= I2C_EEPROM1; end I2C_EEPROM1: begin @@ -297,12 +302,4 @@ i2c_master #(.DW(4)) i2c_master_inst( .rst(rst) ); -`ifdef COCOTB_SIM -initial begin - $dumpfile ("pmod_i2c_master.vcd"); - $dumpvars; - #1; -end -`endif - endmodule diff --git a/gateware/eurorack_pmod.sv b/gateware/eurorack_pmod.sv index ef0b5ab..5202073 100644 --- a/gateware/eurorack_pmod.sv +++ b/gateware/eurorack_pmod.sv @@ -76,6 +76,7 @@ cal #( .W(W), .CAL_MEM_FILE(CAL_MEM_FILE) )cal_instance ( + .rst(rst), .clk_256fs (clk_256fs), .clk_fs (clk_fs), // Calibrated inputs are zeroed if jack is unplugged. @@ -117,12 +118,13 @@ ak4619 ak4619_instance ( .sample_out1 (sample_adc1), .sample_out2 (sample_adc2), .sample_out3 (sample_adc3), - .sample_in0 (force_dac_output == 0 ? sample_dac0 : force_dac_output), - .sample_in1 (force_dac_output == 0 ? sample_dac1 : force_dac_output), - .sample_in2 (force_dac_output == 0 ? sample_dac2 : force_dac_output), - .sample_in3 (force_dac_output == 0 ? sample_dac3 : force_dac_output) + .sample_in0 (sample_dac0), + .sample_in1 (sample_dac1), + .sample_in2 (sample_dac2), + .sample_in3 (sample_dac3) ); + // I2C transceiver and driver for all connected slaves. pmod_i2c_master #( .CODEC_CFG(CODEC_CFG_FILE), diff --git a/gateware/sim/integration/Makefile b/gateware/sim/integration/Makefile new file mode 100644 index 0000000..a1f2751 --- /dev/null +++ b/gateware/sim/integration/Makefile @@ -0,0 +1,18 @@ +SIM ?= icarus +TOPLEVEL_LANG ?= verilog +TOPLEVEL = top +VERILOG_SOURCES = ../../top.sv \ + ../../eurorack_pmod.sv \ + ../../drivers/ak4619.sv \ + ../../cal/cal.sv \ + ../../boards/icebreaker/sysmgr.v \ + ../../drivers/pmod_i2c_master.sv \ + ../../external/no2misc/rtl/i2c_master.v \ + ../../cal/debug_uart.sv \ + ../../external/no2misc/rtl/uart_tx.v \ + ../../cores/mirror.sv + +MODULE = tb_integration +COMPILE_ARGS += -DSELECTED_DSP_CORE=mirror + +include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/gateware/sim/integration/cal/cal_mem.hex b/gateware/sim/integration/cal/cal_mem.hex new file mode 120000 index 0000000..4ba77df --- /dev/null +++ b/gateware/sim/integration/cal/cal_mem.hex @@ -0,0 +1 @@ +../../../cal/cal_mem.hex \ No newline at end of file diff --git a/gateware/sim/integration/drivers/ak4619-cfg.hex b/gateware/sim/integration/drivers/ak4619-cfg.hex new file mode 120000 index 0000000..2f21ea6 --- /dev/null +++ b/gateware/sim/integration/drivers/ak4619-cfg.hex @@ -0,0 +1 @@ +../../../drivers/ak4619-cfg.hex \ No newline at end of file diff --git a/gateware/sim/integration/drivers/pca9635-cfg.hex b/gateware/sim/integration/drivers/pca9635-cfg.hex new file mode 120000 index 0000000..7eb3315 --- /dev/null +++ b/gateware/sim/integration/drivers/pca9635-cfg.hex @@ -0,0 +1 @@ +../../../drivers/pca9635-cfg.hex \ No newline at end of file diff --git a/gateware/sim/integration/tb_integration.py b/gateware/sim/integration/tb_integration.py new file mode 100644 index 0000000..24f74df --- /dev/null +++ b/gateware/sim/integration/tb_integration.py @@ -0,0 +1,62 @@ +import math +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import Timer, FallingEdge, RisingEdge, ClockCycles +from cocotb.handle import Force, Release + + +async def clock_out_word(dut, word): + await FallingEdge(dut.bick) + for i in range(32): + await RisingEdge(dut.bick) + dut.sdout1.value = (word >> (0x1F-i)) & 1 + +async def clock_in_word(dut): + word = 0x00000000 + await RisingEdge(dut.bick) + for i in range(32): + await FallingEdge(dut.bick) + word |= dut.sdin1.value << (0x1F-i) + return word + +def bit_not(n, numbits=16): + return (1 << numbits) - 1 - n + +def signed_to_twos_comp(n, numbits=16): + return n if n >= 0 else bit_not(-n, numbits) + 1 + +@cocotb.test() +async def test_integration_00(dut): + + clk_256fs = Clock(dut.CLK, 83, units='ns') + cocotb.start_soon(clk_256fs.start()) + + dut.eurorack_pmod1.ak4619_instance.sdout1.value = 0 + + dut.sysmgr_instance.pll_lock.value = 0 + await RisingEdge(dut.clk_256fs) + await RisingEdge(dut.clk_256fs) + dut.sysmgr_instance.pll_lock.value = 1 + + dut = dut.eurorack_pmod1.ak4619_instance + + N = 20 + + await FallingEdge(dut.lrck) + + for i in range(N): + + v = signed_to_twos_comp(int(16000*math.sin((2*math.pi*i)/N))) + + await clock_out_word(dut, v << 16) + await clock_out_word(dut, v << 16) + await clock_out_word(dut, v << 16) + await clock_out_word(dut, v << 16) + + # Note: this edge is also where dac_words <= sample_in (sample.sv) + + print("Data clocked from sdout1 present at sample_outX:") + print(hex(dut.sample_out0.value)) + print(hex(dut.sample_out1.value)) + print(hex(dut.sample_out2.value)) + print(hex(dut.sample_out3.value)) diff --git a/gateware/top.sv b/gateware/top.sv index d2f9664..45b1530 100644 --- a/gateware/top.sv +++ b/gateware/top.sv @@ -190,4 +190,13 @@ debug_uart #( .jack(jack) ); +`ifdef COCOTB_SIM +initial begin + $dumpfile ("top.vcd"); + $dumpvars; + #1; +end +`endif + + endmodule