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..cabf051 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,65 +68,82 @@ 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 <= 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. + end + endcase + end end `ifdef COCOTB_SIM + +`ifdef UNIT_TEST initial begin $dumpfile ("cal.vcd"); $dumpvars; @@ -133,4 +151,14 @@ initial begin end `endif +// Shadow fake wires so we can look inside verilog arrays in vcd trace. +generate + genvar idx; + for(idx = 0; idx < 8; idx = idx+1) begin: register + wire [31:0] out_dummy; + assign out_dummy = out[idx]; + end +endgenerate +`endif + endmodule diff --git a/gateware/drivers/ak4619.sv b/gateware/drivers/ak4619.sv index f096386..5596a55 100644 --- a/gateware/drivers/ak4619.sv +++ b/gateware/drivers/ak4619.sv @@ -96,9 +96,6 @@ always_ff @(posedge clk_256fs) begin // BICK transition HI -> LO: Clock in W bits // On HI -> LO both SDIN and SDOUT do not transition. // (determined by AK4619 transition polarity register BCKP) - if (bit_counter == 0) begin - adc_words[channel] <= 0; - end if (bit_counter < W) begin adc_words[channel][W - bit_counter - 1] <= sdout1; end @@ -122,7 +119,9 @@ always_ff @(posedge clk_256fs) begin end end + `ifdef COCOTB_SIM +`ifdef UNIT_TEST initial begin $dumpfile ("ak4619.vcd"); $dumpvars; @@ -130,4 +129,15 @@ initial begin end `endif +// Shadow fake wires so we can look inside verilog arrays in vcd trace. +generate + genvar idx; + for(idx = 0; idx < N_CHANNELS; idx = idx+1) begin: register + wire [W-1:0] adc_dummy; + assign adc_dummy = adc_words[idx]; + end +endgenerate + +`endif + endmodule diff --git a/gateware/drivers/pmod_i2c_master.sv b/gateware/drivers/pmod_i2c_master.sv index 7e3915b..ccc0577 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 @@ -298,11 +303,13 @@ i2c_master #(.DW(4)) i2c_master_inst( ); `ifdef COCOTB_SIM +`ifdef UNIT_TEST initial begin $dumpfile ("pmod_i2c_master.vcd"); $dumpvars; #1; end `endif +`endif endmodule diff --git a/gateware/eurorack_pmod.sv b/gateware/eurorack_pmod.sv index ef0b5ab..03e5849 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. @@ -123,6 +124,7 @@ ak4619 ak4619_instance ( .sample_in3 (force_dac_output == 0 ? sample_dac3 : force_dac_output) ); + // I2C transceiver and driver for all connected slaves. pmod_i2c_master #( .CODEC_CFG(CODEC_CFG_FILE), diff --git a/gateware/sim/ak4619/Makefile b/gateware/sim/ak4619/Makefile index cbfabe2..0560162 100644 --- a/gateware/sim/ak4619/Makefile +++ b/gateware/sim/ak4619/Makefile @@ -2,5 +2,6 @@ SIM ?= icarus TOPLEVEL_LANG ?= verilog VERILOG_SOURCES = ../../drivers/ak4619.sv MODULE = tb_ak4619 +COMPILE_ARGS += -DUNIT_TEST include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/gateware/sim/ak4619/tb_ak4619.py b/gateware/sim/ak4619/tb_ak4619.py index d7cb039..4a013e5 100644 --- a/gateware/sim/ak4619/tb_ak4619.py +++ b/gateware/sim/ak4619/tb_ak4619.py @@ -1,21 +1,12 @@ +import sys 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): - 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 +# Hack to import some helpers despite existing outside a package. +sys.path.append("..") +from util.i2s import * @cocotb.test() async def test_ak4619_00(dut): @@ -37,11 +28,11 @@ async def test_ak4619_00(dut): await RisingEdge(dut.clk_256fs) dut.rst.value = 0 - await FallingEdge(dut.clk_fs) - await clock_out_word(dut, TEST_L0) - await clock_out_word(dut, TEST_R0) - await clock_out_word(dut, TEST_L1) - await clock_out_word(dut, TEST_R1) + await FallingEdge(dut.lrck) + await i2s_clock_out_u32(dut.bick, dut.sdout1, TEST_L0) + await i2s_clock_out_u32(dut.bick, dut.sdout1, TEST_R0) + await i2s_clock_out_u32(dut.bick, dut.sdout1, TEST_L1) + await i2s_clock_out_u32(dut.bick, dut.sdout1, TEST_R1) # Note: this edge is also where dac_words <= sample_in (sample.sv) @@ -66,10 +57,10 @@ async def test_ak4619_00(dut): await FallingEdge(dut.lrck) await FallingEdge(dut.lrck) - result_l0 = await clock_in_word(dut) - result_r0 = await clock_in_word(dut) - result_l1 = await clock_in_word(dut) - result_r1 = await clock_in_word(dut) + result_l0 = await i2s_clock_in_u32(dut.bick, dut.sdin1) + result_r0 = await i2s_clock_in_u32(dut.bick, dut.sdin1) + result_l1 = await i2s_clock_in_u32(dut.bick, dut.sdin1) + result_r1 = await i2s_clock_in_u32(dut.bick, dut.sdin1) print("Data clocked from sample_inX out to sdin1:") print(hex(result_l0)) diff --git a/gateware/sim/cal/Makefile b/gateware/sim/cal/Makefile index 49b3c87..55f10d2 100644 --- a/gateware/sim/cal/Makefile +++ b/gateware/sim/cal/Makefile @@ -2,5 +2,6 @@ SIM ?= icarus TOPLEVEL_LANG ?= verilog VERILOG_SOURCES = ../../cal/cal.sv MODULE = tb_cal +COMPILE_ARGS += -DUNIT_TEST include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/gateware/sim/cal/tb_cal.py b/gateware/sim/cal/tb_cal.py index 7370f18..4bad6b1 100644 --- a/gateware/sim/cal/tb_cal.py +++ b/gateware/sim/cal/tb_cal.py @@ -1,23 +1,18 @@ +import sys import cocotb from cocotb.clock import Clock from cocotb.triggers import Timer, FallingEdge, RisingEdge, ClockCycles from cocotb.handle import Force, Release -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 - -def twos_comp_to_signed(n, numbits=16): - if (1 << (numbits-1) & n) > 0: - return -int(bit_not(n, numbits) + 1) - else: - return n +# Hack to import some helpers despite existing outside a package. +sys.path.append("..") +from util.i2s import * @cocotb.test() async def test_cal_00(dut): + sample_width = 16 + clk_256fs = Clock(dut.clk_256fs, 83, units='ns') clk_fs = Clock(dut.clk_fs, 83*256, units='ns') cocotb.start_soon(clk_256fs.start()) @@ -46,19 +41,24 @@ async def test_cal_00(dut): channel = 0 - for cal_inx, cal_outx in [(dut.in0, dut.out0), - (dut.in1, dut.out1), - (dut.in2, dut.out2), - (dut.in3, dut.out3), - (dut.in4, dut.out4), - (dut.in5, dut.out5), - (dut.in6, dut.out6), - (dut.in7, dut.out7)]: + all_ins_outs = [(dut.in0, dut.out0), + (dut.in1, dut.out1), + (dut.in2, dut.out2), + (dut.in3, dut.out3), + (dut.in4, dut.out4), + (dut.in5, dut.out5), + (dut.in6, dut.out6), + (dut.in7, dut.out7)] + for cal_inx, cal_outx in all_ins_outs: for value in test_values: expect = ((value - cal_mem[channel*2]) * cal_mem[channel*2 + 1]) >> 10 - cal_inx.value = Force(signed_to_twos_comp(value)) + # Default all inputs to zero so we don't have undefined + # values everywhere else in the input array. + for i, o in all_ins_outs: + i.value = Force(0) + cal_inx.value = Force(bits_from_signed(value, sample_width)) if expect > 32000: expect = 32000 if expect < -32000: expect = -32000 print(f"ch={channel}\t{int(value):6d}\t", end="") @@ -66,7 +66,7 @@ async def test_cal_00(dut): await RisingEdge(dut.clk_fs) await RisingEdge(dut.clk_fs) await RisingEdge(dut.clk_fs) - output = twos_comp_to_signed(cal_outx.value) + output = signed_from_bits(cal_outx.value, sample_width) print(f"=>\t{int(output):6d}\t(expect={expect})") cal_inx.value = Release() assert output == expect 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..27332d7 --- /dev/null +++ b/gateware/sim/integration/tb_integration.py @@ -0,0 +1,53 @@ +import sys +import math +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import Timer, FallingEdge, RisingEdge, ClockCycles +from cocotb.handle import Force, Release + +# Hack to import some helpers despite existing outside a package. +sys.path.append("..") +from util.i2s import * + +@cocotb.test() +async def test_integration_00(dut): + + sample_width=16 + + clk_256fs = Clock(dut.CLK, 83, units='ns') + cocotb.start_soon(clk_256fs.start()) + + dut.eurorack_pmod1.ak4619_instance.sdout1.value = 0 + # Simulate all jacks connected so the cal core doesn't zero them + dut.eurorack_pmod1.jack.value = Force(0xFF) + + # The reset timer is downstream of the PLL lock. + # So if we toggle the PLL lock, we are triggering + # a reset from the highest-level part of the system. + dut.sysmgr_instance.pll_lock.value = 0 + await RisingEdge(dut.clk_256fs) + await RisingEdge(dut.clk_256fs) + dut.sysmgr_instance.pll_lock.value = 1 + + ak4619 = dut.eurorack_pmod1.ak4619_instance + + N = 20 + + for i in range(N): + + v = bits_from_signed(int(16000*math.sin((2*math.pi*i)/N)), sample_width) + + await FallingEdge(ak4619.lrck) + + await i2s_clock_out_u32(ak4619.bick, ak4619.sdout1, v << 16) + await i2s_clock_out_u32(ak4619.bick, ak4619.sdout1, v << 16) + await i2s_clock_out_u32(ak4619.bick, ak4619.sdout1, v << 16) + await i2s_clock_out_u32(ak4619.bick, ak4619.sdout1, 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(ak4619.sample_out0.value)) + print(hex(ak4619.sample_out1.value)) + print(hex(ak4619.sample_out2.value)) + print(hex(ak4619.sample_out3.value)) diff --git a/gateware/sim/pmod_i2c_master/Makefile b/gateware/sim/pmod_i2c_master/Makefile index 8ce5ed0..912aa02 100644 --- a/gateware/sim/pmod_i2c_master/Makefile +++ b/gateware/sim/pmod_i2c_master/Makefile @@ -3,5 +3,6 @@ TOPLEVEL_LANG ?= verilog VERILOG_SOURCES = ../../drivers/pmod_i2c_master.sv \ ../../external/no2misc/rtl/i2c_master.v MODULE = tb_pmod_i2c_master +COMPILE_ARGS += -DUNIT_TEST include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/gateware/sim/transpose/tb_transpose.py b/gateware/sim/transpose/tb_transpose.py index 7635061..3eea75c 100644 --- a/gateware/sim/transpose/tb_transpose.py +++ b/gateware/sim/transpose/tb_transpose.py @@ -1,3 +1,4 @@ +import sys import pickle import cocotb import random @@ -6,21 +7,15 @@ from cocotb.triggers import Timer, FallingEdge, RisingEdge, ClockCycles from cocotb.handle import Force, Release -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 - -def twos_comp_to_signed(n, numbits=16): - if (1 << (numbits-1) & n) > 0: - return -int(bit_not(n, numbits) + 1) - else: - return int(n) +# Hack to import some helpers despite existing outside a package. +sys.path.append("..") +from util.i2s import * @cocotb.test() async def test_transpose_00(dut): + sample_width = 16 + clock = Clock(dut.sample_clk, 5, units='us') cocotb.start_soon(clock.start()) @@ -47,8 +42,8 @@ async def test_transpose_00(dut): data_in = int(1000*math.sin(i / 100)) - dut.sample_in.value = signed_to_twos_comp(data_in) - data_out = twos_comp_to_signed(dut.sample_out.value) + dut.sample_in.value = bits_from_signed(data_in, sample_width) + data_out = signed_from_bits(dut.sample_out.value, sample_width) print(f"i={i} in:", data_in) print(f"i={i} out:", data_out) diff --git a/gateware/sim/util/i2s.py b/gateware/sim/util/i2s.py new file mode 100644 index 0000000..ecc3ac3 --- /dev/null +++ b/gateware/sim/util/i2s.py @@ -0,0 +1,35 @@ +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 i2s_clock_out_u32(bick, sdout, word): + """Clock out a 32-bit word over I2S.""" + for i in range(32): + await RisingEdge(bick) + sdout.value = (word >> (0x1F-i)) & 1 + +async def i2s_clock_in_u32(bick, sdin): + """Clock in a 32-bit word over I2S.""" + word = 0x00000000 + await RisingEdge(bick) + for i in range(32): + await FallingEdge(bick) + word |= sdin.value << (0x1F-i) + return word + +def bits_not(n, width): + """Bitwise NOT from positive integer of `width` bits.""" + return (1 << width) - 1 - n + +def bits_from_signed(n, width): + """Bits (2s complement) of `width` from signed integer.""" + return n if n >= 0 else bits_not(-n, width) + 1 + +def signed_from_bits(n, width): + """Signed integer from (2s complement) bits of `width`.""" + if (1 << (width-1) & n) > 0: + return -int(bits_not(n, width) + 1) + else: + return n diff --git a/gateware/sim/vca/tb_vca.py b/gateware/sim/vca/tb_vca.py index 62c15be..6afcc48 100644 --- a/gateware/sim/vca/tb_vca.py +++ b/gateware/sim/vca/tb_vca.py @@ -1,24 +1,19 @@ +import sys import cocotb import random from cocotb.clock import Clock from cocotb.triggers import Timer, FallingEdge, RisingEdge, ClockCycles from cocotb.handle import Force, Release -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 - -def twos_comp_to_signed(n, numbits=16): - if (1 << (numbits-1) & n) > 0: - return -int(bit_not(n, numbits) + 1) - else: - return int(n) +# Hack to import some helpers despite existing outside a package. +sys.path.append("..") +from util.i2s import * @cocotb.test() async def test_vca_00(dut): + sample_width=16 + clock = Clock(dut.sample_clk, 5, units='us') cocotb.start_soon(clock.start()) clock = Clock(dut.clk, 83, units='ns') @@ -35,11 +30,11 @@ async def test_vca_00(dut): for inx in ins: random_sample = random.randint(-30000, 30000) data_in.append(random_sample) - inx.value = signed_to_twos_comp(random_sample) + inx.value = bits_from_signed(random_sample, sample_width) await RisingEdge(dut.sample_clk) - data_out = [twos_comp_to_signed(out.value) for out in outs] + data_out = [signed_from_bits(out.value, sample_width) for out in outs] print(f"i={i} stimulus:", data_in) print(f"i={i} response:", data_out) 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