Skip to content

Commit

Permalink
sim/integration: add top-level simulation [icebreaker] (#47)
Browse files Browse the repository at this point in the history
* sim/integration: add top-level icebreaker integration test

* sim/integration: clean up and fix all other tests

* sim: clean up shared functions in sim/util
  • Loading branch information
vk2seb authored Nov 1, 2023
1 parent fb2793b commit 26746ff
Show file tree
Hide file tree
Showing 19 changed files with 284 additions and 127 deletions.
8 changes: 8 additions & 0 deletions gateware/boards/icebreaker/sysmgr.v
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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)
Expand All @@ -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
136 changes: 82 additions & 54 deletions gateware/cal/cal.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -57,80 +58,107 @@ 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.
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;
#1;
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
16 changes: 13 additions & 3 deletions gateware/drivers/ak4619.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -122,12 +119,25 @@ always_ff @(posedge clk_256fs) begin
end
end


`ifdef COCOTB_SIM
`ifdef UNIT_TEST
initial begin
$dumpfile ("ak4619.vcd");
$dumpvars;
#1;
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
9 changes: 8 additions & 1 deletion gateware/drivers/pmod_i2c_master.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
2 changes: 2 additions & 0 deletions gateware/eurorack_pmod.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions gateware/sim/ak4619/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
35 changes: 13 additions & 22 deletions gateware/sim/ak4619/tb_ak4619.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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)

Expand All @@ -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))
Expand Down
1 change: 1 addition & 0 deletions gateware/sim/cal/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit 26746ff

Please sign in to comment.