diff --git a/gateware/drivers/ak4619.sv b/gateware/drivers/ak4619.sv index c103ac4..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 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/tb_cal.py b/gateware/sim/cal/tb_cal.py index ee117ea..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()) @@ -63,7 +58,7 @@ async def test_cal_00(dut): # values everywhere else in the input array. for i, o in all_ins_outs: i.value = Force(0) - cal_inx.value = Force(signed_to_twos_comp(value)) + 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="") @@ -71,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/tb_integration.py b/gateware/sim/integration/tb_integration.py index 3d43d0d..27332d7 100644 --- a/gateware/sim/integration/tb_integration.py +++ b/gateware/sim/integration/tb_integration.py @@ -1,33 +1,19 @@ +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 - -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 +# 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()) @@ -35,30 +21,33 @@ async def test_integration_00(dut): # 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 - dut = dut.eurorack_pmod1.ak4619_instance + ak4619 = 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))) + v = bits_from_signed(int(16000*math.sin((2*math.pi*i)/N)), sample_width) + + await FallingEdge(ak4619.lrck) - 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) + 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(dut.sample_out0.value)) - print(hex(dut.sample_out1.value)) - print(hex(dut.sample_out2.value)) - print(hex(dut.sample_out3.value)) + 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/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)