|
| 1 | +# amaranth: UnusedElaboratable=no |
| 2 | + |
| 3 | +import unittest |
| 4 | +from amaranth import * |
| 5 | +from amaranth.sim import * |
| 6 | + |
| 7 | +from amaranth_soc import wishbone |
| 8 | +from amaranth_soc.wishbone.sram import WishboneSRAM |
| 9 | + |
| 10 | + |
| 11 | +class WishboneSRAMTestCase(unittest.TestCase): |
| 12 | + def test_init(self): |
| 13 | + # default granularity, writable, no initial values |
| 14 | + dut_1 = WishboneSRAM(size=1024, data_width=32) |
| 15 | + self.assertEqual(dut_1.size, 1024) |
| 16 | + self.assertEqual(dut_1.writable, True) |
| 17 | + self.assertEqual(list(dut_1.init), [0 for _ in range(1024)]) |
| 18 | + self.assertEqual(dut_1.wb_bus.addr_width, 10) |
| 19 | + self.assertEqual(dut_1.wb_bus.data_width, 32) |
| 20 | + self.assertEqual(dut_1.wb_bus.granularity, 32) |
| 21 | + self.assertEqual(dut_1.wb_bus.features, |
| 22 | + {wishbone.Feature.CTI, wishbone.Feature.BTE}) |
| 23 | + self.assertEqual(dut_1.wb_bus.memory_map.addr_width, 10) |
| 24 | + self.assertEqual(dut_1.wb_bus.memory_map.data_width, 32) |
| 25 | + self.assertEqual(dut_1.wb_bus.memory_map.alignment, 0) |
| 26 | + self.assertEqual(list(dut_1.wb_bus.memory_map.resources()), |
| 27 | + [(dut_1._mem, ("mem",), (0, 1024))]) |
| 28 | + # custom granularity, read-only, with initial values |
| 29 | + dut_2 = WishboneSRAM(size=4, data_width=16, granularity=8, writable=False, |
| 30 | + init=(0xbbaa, 0xddcc)) |
| 31 | + self.assertEqual(dut_2.size, 4) |
| 32 | + self.assertEqual(dut_2.writable, False) |
| 33 | + self.assertEqual(list(dut_2.init), [0xbbaa, 0xddcc]) |
| 34 | + self.assertEqual(dut_2.wb_bus.addr_width, 1) |
| 35 | + self.assertEqual(dut_2.wb_bus.data_width, 16) |
| 36 | + self.assertEqual(dut_2.wb_bus.granularity, 8) |
| 37 | + self.assertEqual(dut_2.wb_bus.features, |
| 38 | + {wishbone.Feature.CTI, wishbone.Feature.BTE}) |
| 39 | + self.assertEqual(dut_2.wb_bus.memory_map.addr_width, 2) |
| 40 | + self.assertEqual(dut_2.wb_bus.memory_map.data_width, 8) |
| 41 | + self.assertEqual(dut_2.wb_bus.memory_map.alignment, 0) |
| 42 | + self.assertEqual(list(dut_2.wb_bus.memory_map.resources()), |
| 43 | + [(dut_2._mem, ("mem",), (0, 4))]) |
| 44 | + |
| 45 | + def test_memory_data_init_set(self): |
| 46 | + dut = WishboneSRAM(size=4, data_width=16, granularity=8) |
| 47 | + self.assertEqual(list(dut.init), [0x0000, 0x0000]) |
| 48 | + dut.init = [0xbbaa, 0xddcc] |
| 49 | + self.assertEqual(list(dut._mem_data.init), [0xbbaa, 0xddcc]) |
| 50 | + |
| 51 | + def test_init_wrong_size(self): |
| 52 | + with self.assertRaisesRegex(TypeError, r"Size must be an integer power of two, not 1.0"): |
| 53 | + WishboneSRAM(size=1.0, data_width=32) |
| 54 | + with self.assertRaisesRegex(TypeError, r"Size must be an integer power of two, not 3"): |
| 55 | + WishboneSRAM(size=3, data_width=32) |
| 56 | + |
| 57 | + def test_init_wrong_data_width(self): |
| 58 | + with self.assertRaisesRegex(TypeError, r"Data width must be 8, 16, 32 or 64, not 'foo'"): |
| 59 | + WishboneSRAM(size=1024, data_width="foo") |
| 60 | + with self.assertRaisesRegex(TypeError, r"Data width must be 8, 16, 32 or 64, not 128"): |
| 61 | + WishboneSRAM(size=1024, data_width=128) |
| 62 | + |
| 63 | + def test_init_wrong_granularity(self): |
| 64 | + with self.assertRaisesRegex(TypeError, r"Granularity must be 8, 16, 32 or 64, not 'foo'"): |
| 65 | + WishboneSRAM(size=1024, data_width=32, granularity="foo") |
| 66 | + with self.assertRaisesRegex(TypeError, r"Granularity must be 8, 16, 32 or 64, not 128"): |
| 67 | + WishboneSRAM(size=1024, data_width=32, granularity=128) |
| 68 | + |
| 69 | + def test_init_size_smaller_than_data_width(self): |
| 70 | + with self.assertRaisesRegex(ValueError, |
| 71 | + r"The product of size 2 and granularity 8 must be greater than or equal to data " |
| 72 | + r"width 32, not 16"): |
| 73 | + WishboneSRAM(size=2, data_width=32, granularity=8) |
| 74 | + |
| 75 | + def test_sim_writable(self): |
| 76 | + dut = WishboneSRAM(size=128, data_width=32, granularity=8, writable=True, init=range(32)) |
| 77 | + |
| 78 | + async def wb_cycle(ctx, *, adr, sel, we, dat_w, cti, bte=0, assert_dat_r=None): |
| 79 | + ctx.set(dut.wb_bus.cyc, 1) |
| 80 | + ctx.set(dut.wb_bus.stb, 1) |
| 81 | + ctx.set(dut.wb_bus.adr, adr) |
| 82 | + ctx.set(dut.wb_bus.sel, sel) |
| 83 | + ctx.set(dut.wb_bus.we, we) |
| 84 | + ctx.set(dut.wb_bus.dat_w, dat_w) |
| 85 | + ctx.set(dut.wb_bus.cti, cti) |
| 86 | + ctx.set(dut.wb_bus.bte, bte) |
| 87 | + |
| 88 | + await ctx.tick() |
| 89 | + self.assertEqual(ctx.get(dut.wb_bus.ack), 1) |
| 90 | + if assert_dat_r is not None: |
| 91 | + self.assertEqual(ctx.get(dut.wb_bus.dat_r), assert_dat_r) |
| 92 | + |
| 93 | + ctx.set(dut.wb_bus.cyc, 0) |
| 94 | + ctx.set(dut.wb_bus.stb, 0) |
| 95 | + |
| 96 | + async def testbench(ctx): |
| 97 | + self.assertEqual(ctx.get(dut.wb_bus.ack), 0) |
| 98 | + for i in range(32): |
| 99 | + self.assertEqual(ctx.get(dut._mem_data[i]), i) |
| 100 | + |
| 101 | + # cti = CLASSIC ======================================================================= |
| 102 | + |
| 103 | + # - left shift all values by 24 bits: |
| 104 | + for i in range(32): |
| 105 | + await wb_cycle(ctx, cti=wishbone.CycleType.CLASSIC, |
| 106 | + adr=i, sel=0b1001, we=1, dat_w=(i << 24) | 0x00ffff00, |
| 107 | + assert_dat_r=i) |
| 108 | + await ctx.tick() |
| 109 | + self.assertEqual(ctx.get(dut.wb_bus.ack), 0) |
| 110 | + |
| 111 | + for i in range(32): |
| 112 | + self.assertEqual(ctx.get(dut._mem_data[i]), i << 24) |
| 113 | + |
| 114 | + # cti = INCR_BURST, bte = LINEAR ====================================================== |
| 115 | + |
| 116 | + # - right shift all values by 24 bits: |
| 117 | + for i in range(32): |
| 118 | + cti = wishbone.CycleType.END_OF_BURST if i == 31 else wishbone.CycleType.INCR_BURST |
| 119 | + await wb_cycle(ctx, cti=cti, bte=wishbone.BurstTypeExt.LINEAR, |
| 120 | + adr=i, sel=0b1001, we=1, dat_w=i | 0x00ffff00, |
| 121 | + assert_dat_r=i << 24) |
| 122 | + |
| 123 | + await ctx.tick() |
| 124 | + self.assertEqual(ctx.get(dut.wb_bus.ack), 0) |
| 125 | + for i in range(32): |
| 126 | + self.assertEqual(ctx.get(dut._mem_data[i]), i) |
| 127 | + |
| 128 | + # cti = INCR_BURST, bte = WRAP_4 ====================================================== |
| 129 | + |
| 130 | + # - increment values at addresses 0..15: |
| 131 | + for i in (1,2,3,0, 5,6,7,4, 9,10,11,8, 13,14,15,12): |
| 132 | + cti = wishbone.CycleType.END_OF_BURST if i == 12 else wishbone.CycleType.INCR_BURST |
| 133 | + await wb_cycle(ctx, cti=cti, bte=wishbone.BurstTypeExt.WRAP_4, |
| 134 | + adr=i, sel=0b0001, we=1, dat_w=i + 1, |
| 135 | + assert_dat_r=i) |
| 136 | + |
| 137 | + await ctx.tick() |
| 138 | + self.assertEqual(ctx.get(dut.wb_bus.ack), 0) |
| 139 | + for i in range(16): |
| 140 | + self.assertEqual(ctx.get(dut._mem_data[i]), i + 1) |
| 141 | + |
| 142 | + # cti = INCR_BURST, bte = WRAP_8 ====================================================== |
| 143 | + |
| 144 | + # - increment values at addresses 0..15: |
| 145 | + for i in (1,2,3,4,5,6,7,0, 9,10,11,12,13,14,15,8): |
| 146 | + cti = wishbone.CycleType.END_OF_BURST if i == 8 else wishbone.CycleType.INCR_BURST |
| 147 | + await wb_cycle(ctx, cti=cti, bte=wishbone.BurstTypeExt.WRAP_8, |
| 148 | + adr=i, sel=0b0001, we=1, dat_w=i + 2, |
| 149 | + assert_dat_r=i + 1) |
| 150 | + |
| 151 | + await ctx.tick() |
| 152 | + self.assertEqual(ctx.get(dut.wb_bus.ack), 0) |
| 153 | + for i in range(16): |
| 154 | + self.assertEqual(ctx.get(dut._mem_data[i]), i + 2) |
| 155 | + |
| 156 | + # cti = INCR_BURST, bte = WRAP_16 ===================================================== |
| 157 | + |
| 158 | + # - increment values at addresses 0..15: |
| 159 | + for i in (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0): |
| 160 | + cti = wishbone.CycleType.END_OF_BURST if i == 0 else wishbone.CycleType.INCR_BURST |
| 161 | + await wb_cycle(ctx, cti=cti, bte=wishbone.BurstTypeExt.WRAP_16, |
| 162 | + adr=i, sel=0b0001, we=1, dat_w=i + 3, |
| 163 | + assert_dat_r=i + 2) |
| 164 | + |
| 165 | + await ctx.tick() |
| 166 | + self.assertEqual(ctx.get(dut.wb_bus.ack), 0) |
| 167 | + for i in range(16): |
| 168 | + self.assertEqual(ctx.get(dut._mem_data[i]), i + 3) |
| 169 | + |
| 170 | + # cti = CONST_BURST =================================================================== |
| 171 | + |
| 172 | + # - increment value at address 31, 16 times in a row: |
| 173 | + for i in range(16): |
| 174 | + cti = wishbone.CycleType.END_OF_BURST if i == 15 else wishbone.CycleType.CONST_BURST |
| 175 | + await wb_cycle(ctx, cti=cti, adr=31, sel=0b0001, we=1, dat_w=31 + i + 1, |
| 176 | + assert_dat_r=31 + i) |
| 177 | + |
| 178 | + await ctx.tick() |
| 179 | + self.assertEqual(ctx.get(dut.wb_bus.ack), 0) |
| 180 | + self.assertEqual(ctx.get(dut._mem_data[31]), 31 + 16) |
| 181 | + |
| 182 | + sim = Simulator(dut) |
| 183 | + sim.add_clock(1e-6) |
| 184 | + sim.add_testbench(testbench) |
| 185 | + with sim.write_vcd(vcd_file="test.vcd"): |
| 186 | + sim.run() |
| 187 | + |
| 188 | + def test_sim_readonly(self): |
| 189 | + dut = WishboneSRAM(size=128, data_width=32, granularity=8, writable=False, init=range(32)) |
| 190 | + |
| 191 | + async def testbench(ctx): |
| 192 | + for i in range(32): |
| 193 | + self.assertEqual(ctx.get(dut._mem_data[i]), i) |
| 194 | + |
| 195 | + for i in range(32): |
| 196 | + ctx.set(dut.wb_bus.cyc, 1) |
| 197 | + ctx.set(dut.wb_bus.stb, 1) |
| 198 | + ctx.set(dut.wb_bus.adr, i) |
| 199 | + ctx.set(dut.wb_bus.sel, 0xf) |
| 200 | + ctx.set(dut.wb_bus.we, 1) |
| 201 | + ctx.set(dut.wb_bus.dat_w, 0xffffffff) |
| 202 | + await ctx.tick().until(dut.wb_bus.ack) |
| 203 | + ctx.set(dut.wb_bus.cyc, 0) |
| 204 | + ctx.set(dut.wb_bus.stb, 0) |
| 205 | + await ctx.tick() |
| 206 | + |
| 207 | + for i in range(32): |
| 208 | + self.assertEqual(ctx.get(dut._mem_data[i]), i) |
| 209 | + |
| 210 | + sim = Simulator(dut) |
| 211 | + sim.add_clock(1e-6) |
| 212 | + sim.add_testbench(testbench) |
| 213 | + with sim.write_vcd(vcd_file="test.vcd"): |
| 214 | + sim.run() |
0 commit comments