From abbe227654c29f630413541b60297595cc47f0b3 Mon Sep 17 00:00:00 2001 From: Wanda Date: Fri, 20 Oct 2023 13:40:32 +0200 Subject: [PATCH 1/4] applet: extract test code to a separate file. Fixes #447. --- software/glasgow/applet/audio/dac/__init__.py | 7 - software/glasgow/applet/audio/dac/test.py | 8 ++ .../applet/audio/yamaha_opx/__init__.py | 15 -- .../glasgow/applet/audio/yamaha_opx/test.py | 16 +++ .../applet/display/hd44780/__init__.py | 7 - .../glasgow/applet/display/hd44780/test.py | 8 ++ .../glasgow/applet/display/pdi/__init__.py | 7 - software/glasgow/applet/display/pdi/test.py | 8 ++ .../applet/interface/analyzer/__init__.py | 7 - .../glasgow/applet/interface/analyzer/test.py | 8 ++ .../interface/i2c_initiator/__init__.py | 7 - .../applet/interface/i2c_initiator/test.py | 8 ++ .../applet/interface/i2c_target/__init__.py | 7 - .../applet/interface/i2c_target/test.py | 8 ++ .../applet/interface/jtag_openocd/__init__.py | 7 - .../applet/interface/jtag_openocd/test.py | 8 ++ .../applet/interface/jtag_pinout/__init__.py | 7 - .../applet/interface/jtag_pinout/test.py | 8 ++ .../applet/interface/jtag_probe/__init__.py | 130 ----------------- .../applet/interface/jtag_probe/test.py | 131 ++++++++++++++++++ .../applet/interface/ps2_host/__init__.py | 7 - .../glasgow/applet/interface/ps2_host/test.py | 8 ++ .../applet/interface/sbw_probe/__init__.py | 7 - .../applet/interface/sbw_probe/test.py | 8 ++ .../interface/spi_controller/__init__.py | 30 ---- .../applet/interface/spi_controller/test.py | 33 +++++ .../glasgow/applet/interface/uart/__init__.py | 20 --- .../glasgow/applet/interface/uart/test.py | 23 +++ .../applet/internal/benchmark/__init__.py | 7 - .../glasgow/applet/internal/benchmark/test.py | 8 ++ .../applet/internal/selftest/__init__.py | 7 - .../glasgow/applet/internal/selftest/test.py | 8 ++ .../glasgow/applet/memory/_25x/__init__.py | 131 ------------------ software/glasgow/applet/memory/_25x/test.py | 131 ++++++++++++++++++ .../glasgow/applet/memory/floppy/__init__.py | 7 - software/glasgow/applet/memory/floppy/test.py | 8 ++ .../glasgow/applet/memory/onfi/__init__.py | 7 - software/glasgow/applet/memory/onfi/test.py | 8 ++ .../glasgow/applet/memory/prom/__init__.py | 7 - software/glasgow/applet/memory/prom/test.py | 8 ++ .../applet/program/avr/spi/__init__.py | 89 ------------ .../glasgow/applet/program/avr/spi/test.py | 88 ++++++++++++ .../applet/program/ice40_flash/__init__.py | 7 - .../applet/program/ice40_flash/test.py | 8 ++ .../applet/program/ice40_sram/__init__.py | 9 -- .../glasgow/applet/program/ice40_sram/test.py | 10 ++ .../glasgow/applet/program/m16c/__init__.py | 7 - software/glasgow/applet/program/m16c/test.py | 8 ++ .../applet/program/nrf24lx1/__init__.py | 7 - .../glasgow/applet/program/nrf24lx1/test.py | 9 ++ .../glasgow/applet/radio/nrf24l01/__init__.py | 7 - .../glasgow/applet/radio/nrf24l01/test.py | 8 ++ .../glasgow/applet/sensor/hx711/__init__.py | 7 - software/glasgow/applet/sensor/hx711/test.py | 8 ++ .../glasgow/applet/sensor/pmsx003/__init__.py | 7 - .../glasgow/applet/sensor/pmsx003/test.py | 8 ++ .../applet/video/hub75_output/__init__.py | 7 - .../glasgow/applet/video/hub75_output/test.py | 8 ++ .../applet/video/rgb_input/__init__.py | 9 -- .../glasgow/applet/video/rgb_input/test.py | 10 ++ .../applet/video/vga_output/__init__.py | 7 - .../glasgow/applet/video/vga_output/test.py | 8 ++ .../applet/video/ws2812_output/__init__.py | 7 - .../applet/video/ws2812_output/test.py | 8 ++ 64 files changed, 635 insertions(+), 601 deletions(-) create mode 100644 software/glasgow/applet/audio/dac/test.py create mode 100644 software/glasgow/applet/audio/yamaha_opx/test.py create mode 100644 software/glasgow/applet/display/hd44780/test.py create mode 100644 software/glasgow/applet/display/pdi/test.py create mode 100644 software/glasgow/applet/interface/analyzer/test.py create mode 100644 software/glasgow/applet/interface/i2c_initiator/test.py create mode 100644 software/glasgow/applet/interface/i2c_target/test.py create mode 100644 software/glasgow/applet/interface/jtag_openocd/test.py create mode 100644 software/glasgow/applet/interface/jtag_pinout/test.py create mode 100644 software/glasgow/applet/interface/jtag_probe/test.py create mode 100644 software/glasgow/applet/interface/ps2_host/test.py create mode 100644 software/glasgow/applet/interface/sbw_probe/test.py create mode 100644 software/glasgow/applet/interface/spi_controller/test.py create mode 100644 software/glasgow/applet/interface/uart/test.py create mode 100644 software/glasgow/applet/internal/benchmark/test.py create mode 100644 software/glasgow/applet/internal/selftest/test.py create mode 100644 software/glasgow/applet/memory/_25x/test.py create mode 100644 software/glasgow/applet/memory/floppy/test.py create mode 100644 software/glasgow/applet/memory/onfi/test.py create mode 100644 software/glasgow/applet/memory/prom/test.py create mode 100644 software/glasgow/applet/program/avr/spi/test.py create mode 100644 software/glasgow/applet/program/ice40_flash/test.py create mode 100644 software/glasgow/applet/program/ice40_sram/test.py create mode 100644 software/glasgow/applet/program/m16c/test.py create mode 100644 software/glasgow/applet/program/nrf24lx1/test.py create mode 100644 software/glasgow/applet/radio/nrf24l01/test.py create mode 100644 software/glasgow/applet/sensor/hx711/test.py create mode 100644 software/glasgow/applet/sensor/pmsx003/test.py create mode 100644 software/glasgow/applet/video/hub75_output/test.py create mode 100644 software/glasgow/applet/video/rgb_input/test.py create mode 100644 software/glasgow/applet/video/vga_output/test.py create mode 100644 software/glasgow/applet/video/ws2812_output/test.py diff --git a/software/glasgow/applet/audio/dac/__init__.py b/software/glasgow/applet/audio/dac/__init__.py index 095c33d38..8e4abdb26 100644 --- a/software/glasgow/applet/audio/dac/__init__.py +++ b/software/glasgow/applet/audio/dac/__init__.py @@ -233,10 +233,3 @@ async def interact(self, device, args, pcm_iface): data = await reader.read(512) await pcm_iface.write(data) await pcm_iface.flush(wait=False) - -# ------------------------------------------------------------------------------------------------- - -class AudioDACAppletTestCase(GlasgowAppletTestCase, applet=AudioDACApplet): - @synthesis_test - def test_build(self): - self.assertBuilds(args=["--unsigned"]) diff --git a/software/glasgow/applet/audio/dac/test.py b/software/glasgow/applet/audio/dac/test.py new file mode 100644 index 000000000..50a7d78c2 --- /dev/null +++ b/software/glasgow/applet/audio/dac/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import AudioDACApplet + + +class AudioDACAppletTestCase(GlasgowAppletTestCase, applet=AudioDACApplet): + @synthesis_test + def test_build(self): + self.assertBuilds(args=["--unsigned"]) diff --git a/software/glasgow/applet/audio/yamaha_opx/__init__.py b/software/glasgow/applet/audio/yamaha_opx/__init__.py index 172014544..ade224030 100644 --- a/software/glasgow/applet/audio/yamaha_opx/__init__.py +++ b/software/glasgow/applet/audio/yamaha_opx/__init__.py @@ -1248,18 +1248,3 @@ async def set_voltage(voltage): for fut in done: await fut args.pcm_file.write(record_fut.result()) - -# ------------------------------------------------------------------------------------------------- - -class AudioYamahaOPxAppletTestCase(GlasgowAppletTestCase, applet=AudioYamahaOPxApplet): - @synthesis_test - def test_build_opl2(self): - self.assertBuilds(args=["--device", "OPL2"]) - - @synthesis_test - def test_build_opl3(self): - self.assertBuilds(args=["--device", "OPL3"]) - - @synthesis_test - def test_build_opm(self): - self.assertBuilds(args=["--device", "OPM"]) diff --git a/software/glasgow/applet/audio/yamaha_opx/test.py b/software/glasgow/applet/audio/yamaha_opx/test.py new file mode 100644 index 000000000..ef98cb0a6 --- /dev/null +++ b/software/glasgow/applet/audio/yamaha_opx/test.py @@ -0,0 +1,16 @@ +from ... import * +from . import AudioYamahaOPxApplet + + +class AudioYamahaOPxAppletTestCase(GlasgowAppletTestCase, applet=AudioYamahaOPxApplet): + @synthesis_test + def test_build_opl2(self): + self.assertBuilds(args=["--device", "OPL2"]) + + @synthesis_test + def test_build_opl3(self): + self.assertBuilds(args=["--device", "OPL3"]) + + @synthesis_test + def test_build_opm(self): + self.assertBuilds(args=["--device", "OPM"]) diff --git a/software/glasgow/applet/display/hd44780/__init__.py b/software/glasgow/applet/display/hd44780/__init__.py index e4f1fcaaa..839db97a6 100644 --- a/software/glasgow/applet/display/hd44780/__init__.py +++ b/software/glasgow/applet/display/hd44780/__init__.py @@ -278,10 +278,3 @@ async def data(bytes): await cmd(CMD_DDRAM_ADDRESS|0x40) await data(datetime.now().strftime("%y-%m-%d").encode("ascii")) await iface.flush() - -# ------------------------------------------------------------------------------------------------- - -class DisplayHD44780AppletTestCase(GlasgowAppletTestCase, applet=DisplayHD44780Applet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/display/hd44780/test.py b/software/glasgow/applet/display/hd44780/test.py new file mode 100644 index 000000000..cee470986 --- /dev/null +++ b/software/glasgow/applet/display/hd44780/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import DisplayHD44780Applet + + +class DisplayHD44780AppletTestCase(GlasgowAppletTestCase, applet=DisplayHD44780Applet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/display/pdi/__init__.py b/software/glasgow/applet/display/pdi/__init__.py index ca7ec7eda..a324704c7 100644 --- a/software/glasgow/applet/display/pdi/__init__.py +++ b/software/glasgow/applet/display/pdi/__init__.py @@ -476,10 +476,3 @@ async def interact(self, device, args, pdi_iface): await pdi_iface.display_frame(mode="white", time_ms=stage_ms, image=image) await pdi_iface.display_frame(mode="white", time_ms=stage_ms, image=image) await pdi_iface.power_off() - -# ------------------------------------------------------------------------------------------------- - -class DisplayPDIAppletTestCase(GlasgowAppletTestCase, applet=DisplayPDIApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/display/pdi/test.py b/software/glasgow/applet/display/pdi/test.py new file mode 100644 index 000000000..93148b183 --- /dev/null +++ b/software/glasgow/applet/display/pdi/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import DisplayPDIApplet + + +class DisplayPDIAppletTestCase(GlasgowAppletTestCase, applet=DisplayPDIApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/interface/analyzer/__init__.py b/software/glasgow/applet/interface/analyzer/__init__.py index 28af70c5b..a2e7f7da3 100644 --- a/software/glasgow/applet/interface/analyzer/__init__.py +++ b/software/glasgow/applet/interface/analyzer/__init__.py @@ -124,10 +124,3 @@ async def interact(self, device, args, iface): finally: vcd_writer.close(timestamp) - -# ------------------------------------------------------------------------------------------------- - -class AnalyzerAppletTestCase(GlasgowAppletTestCase, applet=AnalyzerApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/interface/analyzer/test.py b/software/glasgow/applet/interface/analyzer/test.py new file mode 100644 index 000000000..114bacd74 --- /dev/null +++ b/software/glasgow/applet/interface/analyzer/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import AnalyzerApplet + + +class AnalyzerAppletTestCase(GlasgowAppletTestCase, applet=AnalyzerApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/interface/i2c_initiator/__init__.py b/software/glasgow/applet/interface/i2c_initiator/__init__.py index 49b630d72..e4b77dbd4 100644 --- a/software/glasgow/applet/interface/i2c_initiator/__init__.py +++ b/software/glasgow/applet/interface/i2c_initiator/__init__.py @@ -347,10 +347,3 @@ async def interact(self, device, args, i2c_iface): manufacturer, part_ident, revision = device_id self.logger.info("device %s ID: manufacturer %s, part %s, revision %s", bin(addr), bin(manufacturer), bin(part_ident), bin(revision)) - -# ------------------------------------------------------------------------------------------------- - -class I2CInitiatorAppletTestCase(GlasgowAppletTestCase, applet=I2CInitiatorApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/interface/i2c_initiator/test.py b/software/glasgow/applet/interface/i2c_initiator/test.py new file mode 100644 index 000000000..4e93f8cfe --- /dev/null +++ b/software/glasgow/applet/interface/i2c_initiator/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import I2CInitiatorApplet + + +class I2CInitiatorAppletTestCase(GlasgowAppletTestCase, applet=I2CInitiatorApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/interface/i2c_target/__init__.py b/software/glasgow/applet/interface/i2c_target/__init__.py index 494ca5885..fa399a24f 100644 --- a/software/glasgow/applet/interface/i2c_target/__init__.py +++ b/software/glasgow/applet/interface/i2c_target/__init__.py @@ -248,10 +248,3 @@ async def run(self, device, args): async def interact(self, device, args, iface): while True: await iface.read_event() - -# ------------------------------------------------------------------------------------------------- - -class I2CTargetAppletTestCase(GlasgowAppletTestCase, applet=I2CTargetApplet): - @synthesis_test - def test_build(self): - self.assertBuilds(args=["-A", "0b1010000"]) diff --git a/software/glasgow/applet/interface/i2c_target/test.py b/software/glasgow/applet/interface/i2c_target/test.py new file mode 100644 index 000000000..f3f89a1ab --- /dev/null +++ b/software/glasgow/applet/interface/i2c_target/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import I2CTargetApplet + + +class I2CTargetAppletTestCase(GlasgowAppletTestCase, applet=I2CTargetApplet): + @synthesis_test + def test_build(self): + self.assertBuilds(args=["-A", "0b1010000"]) diff --git a/software/glasgow/applet/interface/jtag_openocd/__init__.py b/software/glasgow/applet/interface/jtag_openocd/__init__.py index e32120d59..b3b61bd97 100644 --- a/software/glasgow/applet/interface/jtag_openocd/__init__.py +++ b/software/glasgow/applet/interface/jtag_openocd/__init__.py @@ -138,10 +138,3 @@ async def forward_in(): forward_in_fut = asyncio.ensure_future(forward_in()) await asyncio.wait([forward_out_fut, forward_in_fut], return_when=asyncio.FIRST_EXCEPTION) - -# ------------------------------------------------------------------------------------------------- - -class JTAGOpenOCDAppletTestCase(GlasgowAppletTestCase, applet=JTAGOpenOCDApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/interface/jtag_openocd/test.py b/software/glasgow/applet/interface/jtag_openocd/test.py new file mode 100644 index 000000000..fb37cac7d --- /dev/null +++ b/software/glasgow/applet/interface/jtag_openocd/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import JTAGOpenOCDApplet + + +class JTAGOpenOCDAppletTestCase(GlasgowAppletTestCase, applet=JTAGOpenOCDApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/interface/jtag_pinout/__init__.py b/software/glasgow/applet/interface/jtag_pinout/__init__.py index 58833ff10..a9981e1a0 100644 --- a/software/glasgow/applet/interface/jtag_pinout/__init__.py +++ b/software/glasgow/applet/interface/jtag_pinout/__init__.py @@ -447,10 +447,3 @@ def bits_to_str(pins): else: self.logger.warning("more than one JTAG interface detected; this is likely a false " "positive") - -# ------------------------------------------------------------------------------------------------- - -class JTAGPinoutAppletTestCase(GlasgowAppletTestCase, applet=JTAGPinoutApplet): - @synthesis_test - def test_build(self): - self.assertBuilds(args=["--pins-jtag", "0:3"]) diff --git a/software/glasgow/applet/interface/jtag_pinout/test.py b/software/glasgow/applet/interface/jtag_pinout/test.py new file mode 100644 index 000000000..ca3e1423d --- /dev/null +++ b/software/glasgow/applet/interface/jtag_pinout/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import JTAGPinoutApplet + + +class JTAGPinoutAppletTestCase(GlasgowAppletTestCase, applet=JTAGPinoutApplet): + @synthesis_test + def test_build(self): + self.assertBuilds(args=["--pins-jtag", "0:3"]) diff --git a/software/glasgow/applet/interface/jtag_probe/__init__.py b/software/glasgow/applet/interface/jtag_probe/__init__.py index e047e9c9f..c0d4483df 100644 --- a/software/glasgow/applet/interface/jtag_probe/__init__.py +++ b/software/glasgow/applet/interface/jtag_probe/__init__.py @@ -1099,133 +1099,3 @@ async def repl(self, device, args, jtag_iface): locals={"iface":iface}, run_callback=jtag_iface.flush ).interact() - -# ------------------------------------------------------------------------------------------------- - -import unittest - - -class JTAGInterrogationTestCase(unittest.TestCase): - def setUp(self): - self.iface = JTAGProbeInterface(interface=None, logger=JTAGProbeApplet.logger) - - def test_dr_empty(self): - self.assertEqual(self.iface.interrogate_dr(bits("")), []) - - def test_dr_bypass(self): - self.assertEqual(self.iface.interrogate_dr(bits("0")), [None]) - - def test_dr_idcode(self): - dr = bits("00111011101000000000010001110111") - self.assertEqual(self.iface.interrogate_dr(dr), [0x3ba00477]) - - def test_dr_truncated(self): - dr = bits("0011101110100000000001000111011") - with self.assertRaisesRegex(JTAGProbeError, - r"^TAP #0 has truncated DR IDCODE=<1101110001000000000010111011100>$"): - self.iface.interrogate_dr(dr) - self.assertEqual(self.iface.interrogate_dr(dr, check=False), None) - - def test_dr_bypass_idcode(self): - dr = bits("001110111010000000000100011101110") - self.assertEqual(self.iface.interrogate_dr(dr), [None, 0x3ba00477]) - - def test_dr_idcode_bypass(self): - dr = bits("000111011101000000000010001110111") - self.assertEqual(self.iface.interrogate_dr(dr), [0x3ba00477, None]) - - def test_dr_invalid(self): - dr = bits("00000000000000000000000011111111") - with self.assertRaisesRegex(JTAGProbeError, - r"^TAP #0 has invalid DR IDCODE=000000ff$"): - self.iface.interrogate_dr(dr) - self.assertEqual(self.iface.interrogate_dr(dr, check=False), None) - - def test_ir_1tap_0start(self): - ir = bits("0100") - with self.assertRaisesRegex(JTAGProbeError, - r"^IR capture does not start with <10> transition$"): - self.iface.interrogate_ir(ir, 1) - self.assertEqual(self.iface.interrogate_ir(ir, 1, check=False), - None) - - def test_ir_1tap_0start_1length(self): - ir = bits("0100") - with self.assertRaisesRegex(JTAGProbeError, - r"^IR capture does not start with <10> transition$"): - self.iface.interrogate_ir(ir, 1, ir_lengths=[4]) - self.assertEqual(self.iface.interrogate_ir(ir, 1, ir_lengths=[4], check=False), - None) - - def test_ir_1tap_1start(self): - ir = bits("0001") - self.assertEqual(self.iface.interrogate_ir(ir, 1), - [4]) - - def test_ir_1tap_2start(self): - ir = bits("0101") - self.assertEqual(self.iface.interrogate_ir(ir, 1), - [4]) - - def test_ir_1tap_2start_1length(self): - ir = bits("0101") - self.assertEqual(self.iface.interrogate_ir(ir, 1, ir_lengths=[4]), - [4]) - - def test_ir_1tap_2start_1length_over(self): - ir = bits("0101") - with self.assertRaisesRegex(JTAGProbeError, - r"^IR capture length differs from sum of IR lengths$"): - self.iface.interrogate_ir(ir, 1, ir_lengths=[5]) - self.assertEqual(self.iface.interrogate_ir(ir, 1, ir_lengths=[5], check=False), - None) - - def test_ir_2tap_1start(self): - ir = bits("0001") - with self.assertRaisesRegex(JTAGProbeError, - r"^IR capture has fewer <10> transitions than TAPs$"): - self.iface.interrogate_ir(ir, 2) - self.assertEqual(self.iface.interrogate_ir(ir, 2, check=False), - None) - - def test_ir_2tap_1start_2length(self): - ir = bits("0001") - with self.assertRaisesRegex(JTAGProbeError, - r"^IR capture has fewer <10> transitions than TAPs$"): - self.iface.interrogate_ir(ir, 2, ir_lengths=[2, 2]) - self.assertEqual(self.iface.interrogate_ir(ir, 2, ir_lengths=[2, 2], check=False), - None) - - def test_ir_2tap_2start(self): - ir = bits("01001") - self.assertEqual(self.iface.interrogate_ir(ir, 2), - [3, 2]) - - def test_ir_2tap_3start(self): - ir = bits("01001001") - with self.assertRaisesRegex(JTAGProbeError, - r"^IR capture insufficiently constrains IR lengths$"): - self.iface.interrogate_ir(ir, 2) - self.assertEqual(self.iface.interrogate_ir(ir, 2, check=False), - None) - - def test_ir_2tap_3start_1length(self): - ir = bits("01001001") - with self.assertRaisesRegex(JTAGProbeError, - r"^IR length count differs from TAP count$"): - self.iface.interrogate_ir(ir, 3, ir_lengths=[1]) - self.assertEqual(self.iface.interrogate_ir(ir, 3, ir_lengths=[1], check=False), - None) - - def test_ir_2tap_3start_2length(self): - ir = bits("01001001") - self.assertEqual(self.iface.interrogate_ir(ir, 2, ir_lengths=[6, 2]), - [6, 2]) - self.assertEqual(self.iface.interrogate_ir(ir, 2, ir_lengths=[3, 5]), - [3, 5]) - - -class JTAGProbeAppletTestCase(GlasgowAppletTestCase, applet=JTAGProbeApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/interface/jtag_probe/test.py b/software/glasgow/applet/interface/jtag_probe/test.py new file mode 100644 index 000000000..74e08f66e --- /dev/null +++ b/software/glasgow/applet/interface/jtag_probe/test.py @@ -0,0 +1,131 @@ +import unittest + +from ....support.bits import * +from ... import * +from . import JTAGProbeApplet, JTAGProbeInterface, JTAGProbeError + + +class JTAGInterrogationTestCase(unittest.TestCase): + def setUp(self): + self.iface = JTAGProbeInterface(interface=None, logger=JTAGProbeApplet.logger) + + def test_dr_empty(self): + self.assertEqual(self.iface.interrogate_dr(bits("")), []) + + def test_dr_bypass(self): + self.assertEqual(self.iface.interrogate_dr(bits("0")), [None]) + + def test_dr_idcode(self): + dr = bits("00111011101000000000010001110111") + self.assertEqual(self.iface.interrogate_dr(dr), [0x3ba00477]) + + def test_dr_truncated(self): + dr = bits("0011101110100000000001000111011") + with self.assertRaisesRegex(JTAGProbeError, + r"^TAP #0 has truncated DR IDCODE=<1101110001000000000010111011100>$"): + self.iface.interrogate_dr(dr) + self.assertEqual(self.iface.interrogate_dr(dr, check=False), None) + + def test_dr_bypass_idcode(self): + dr = bits("001110111010000000000100011101110") + self.assertEqual(self.iface.interrogate_dr(dr), [None, 0x3ba00477]) + + def test_dr_idcode_bypass(self): + dr = bits("000111011101000000000010001110111") + self.assertEqual(self.iface.interrogate_dr(dr), [0x3ba00477, None]) + + def test_dr_invalid(self): + dr = bits("00000000000000000000000011111111") + with self.assertRaisesRegex(JTAGProbeError, + r"^TAP #0 has invalid DR IDCODE=000000ff$"): + self.iface.interrogate_dr(dr) + self.assertEqual(self.iface.interrogate_dr(dr, check=False), None) + + def test_ir_1tap_0start(self): + ir = bits("0100") + with self.assertRaisesRegex(JTAGProbeError, + r"^IR capture does not start with <10> transition$"): + self.iface.interrogate_ir(ir, 1) + self.assertEqual(self.iface.interrogate_ir(ir, 1, check=False), + None) + + def test_ir_1tap_0start_1length(self): + ir = bits("0100") + with self.assertRaisesRegex(JTAGProbeError, + r"^IR capture does not start with <10> transition$"): + self.iface.interrogate_ir(ir, 1, ir_lengths=[4]) + self.assertEqual(self.iface.interrogate_ir(ir, 1, ir_lengths=[4], check=False), + None) + + def test_ir_1tap_1start(self): + ir = bits("0001") + self.assertEqual(self.iface.interrogate_ir(ir, 1), + [4]) + + def test_ir_1tap_2start(self): + ir = bits("0101") + self.assertEqual(self.iface.interrogate_ir(ir, 1), + [4]) + + def test_ir_1tap_2start_1length(self): + ir = bits("0101") + self.assertEqual(self.iface.interrogate_ir(ir, 1, ir_lengths=[4]), + [4]) + + def test_ir_1tap_2start_1length_over(self): + ir = bits("0101") + with self.assertRaisesRegex(JTAGProbeError, + r"^IR capture length differs from sum of IR lengths$"): + self.iface.interrogate_ir(ir, 1, ir_lengths=[5]) + self.assertEqual(self.iface.interrogate_ir(ir, 1, ir_lengths=[5], check=False), + None) + + def test_ir_2tap_1start(self): + ir = bits("0001") + with self.assertRaisesRegex(JTAGProbeError, + r"^IR capture has fewer <10> transitions than TAPs$"): + self.iface.interrogate_ir(ir, 2) + self.assertEqual(self.iface.interrogate_ir(ir, 2, check=False), + None) + + def test_ir_2tap_1start_2length(self): + ir = bits("0001") + with self.assertRaisesRegex(JTAGProbeError, + r"^IR capture has fewer <10> transitions than TAPs$"): + self.iface.interrogate_ir(ir, 2, ir_lengths=[2, 2]) + self.assertEqual(self.iface.interrogate_ir(ir, 2, ir_lengths=[2, 2], check=False), + None) + + def test_ir_2tap_2start(self): + ir = bits("01001") + self.assertEqual(self.iface.interrogate_ir(ir, 2), + [3, 2]) + + def test_ir_2tap_3start(self): + ir = bits("01001001") + with self.assertRaisesRegex(JTAGProbeError, + r"^IR capture insufficiently constrains IR lengths$"): + self.iface.interrogate_ir(ir, 2) + self.assertEqual(self.iface.interrogate_ir(ir, 2, check=False), + None) + + def test_ir_2tap_3start_1length(self): + ir = bits("01001001") + with self.assertRaisesRegex(JTAGProbeError, + r"^IR length count differs from TAP count$"): + self.iface.interrogate_ir(ir, 3, ir_lengths=[1]) + self.assertEqual(self.iface.interrogate_ir(ir, 3, ir_lengths=[1], check=False), + None) + + def test_ir_2tap_3start_2length(self): + ir = bits("01001001") + self.assertEqual(self.iface.interrogate_ir(ir, 2, ir_lengths=[6, 2]), + [6, 2]) + self.assertEqual(self.iface.interrogate_ir(ir, 2, ir_lengths=[3, 5]), + [3, 5]) + + +class JTAGProbeAppletTestCase(GlasgowAppletTestCase, applet=JTAGProbeApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/interface/ps2_host/__init__.py b/software/glasgow/applet/interface/ps2_host/__init__.py index 579617dbc..7b648bbfb 100644 --- a/software/glasgow/applet/interface/ps2_host/__init__.py +++ b/software/glasgow/applet/interface/ps2_host/__init__.py @@ -439,10 +439,3 @@ async def interact(self, device, args, iface): async def print_byte(byte): print("{:02x}".format(byte), end=" ", flush=True) await iface.stream(print_byte) - -# ------------------------------------------------------------------------------------------------- - -class PS2HostAppletTestCase(GlasgowAppletTestCase, applet=PS2HostApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/interface/ps2_host/test.py b/software/glasgow/applet/interface/ps2_host/test.py new file mode 100644 index 000000000..40c6520b8 --- /dev/null +++ b/software/glasgow/applet/interface/ps2_host/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import PS2HostApplet + + +class PS2HostAppletTestCase(GlasgowAppletTestCase, applet=PS2HostApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/interface/sbw_probe/__init__.py b/software/glasgow/applet/interface/sbw_probe/__init__.py index 0771a6de0..8f1ccee1a 100644 --- a/software/glasgow/applet/interface/sbw_probe/__init__.py +++ b/software/glasgow/applet/interface/sbw_probe/__init__.py @@ -232,10 +232,3 @@ async def interact(self, device, args, sbw_iface): self.logger.error("no target detected; connection problem?") else: self.logger.info("found MSP430 core with JTAG ID %#04x", jtag_id) - -# ------------------------------------------------------------------------------------------------- - -class SpyBiWireProbeAppletTestCase(GlasgowAppletTestCase, applet=SpyBiWireProbeApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/interface/sbw_probe/test.py b/software/glasgow/applet/interface/sbw_probe/test.py new file mode 100644 index 000000000..aa7517333 --- /dev/null +++ b/software/glasgow/applet/interface/sbw_probe/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import SpyBiWireProbeApplet + + +class SpyBiWireProbeAppletTestCase(GlasgowAppletTestCase, applet=SpyBiWireProbeApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/interface/spi_controller/__init__.py b/software/glasgow/applet/interface/spi_controller/__init__.py index 39ffbfd40..090873596 100644 --- a/software/glasgow/applet/interface/spi_controller/__init__.py +++ b/software/glasgow/applet/interface/spi_controller/__init__.py @@ -1,6 +1,5 @@ import struct import logging -import types import math from amaranth import * from amaranth.lib.cdc import FFSynchronizer @@ -371,32 +370,3 @@ def hex(arg): return bytes.fromhex(arg) async def interact(self, device, args, spi_iface): data = await spi_iface.transfer(args.data) print(data.hex()) - -# ------------------------------------------------------------------------------------------------- - -class SPIControllerAppletTestCase(GlasgowAppletTestCase, applet=SPIControllerApplet): - @synthesis_test - def test_build(self): - self.assertBuilds(args=["--pin-sck", "0", "--pin-cs", "1", - "--pin-copi", "2", "--pin-cipo", "3"]) - - def setup_loopback(self): - self.build_simulated_applet() - mux_iface = self.applet.mux_interface - m = Module() - m.d.comb += mux_iface.pads.cipo_t.i.eq(mux_iface.pads.copi_t.o) - self.target.add_submodule(m) - - @applet_simulation_test("setup_loopback", - ["--pin-sck", "0", "--pin-cs", "1", - "--pin-copi", "2", "--pin-cipo", "3", - "--frequency", "5000"]) - @types.coroutine - def test_loopback(self): - mux_iface = self.applet.mux_interface - spi_iface = yield from self.run_simulated_applet() - - self.assertEqual((yield mux_iface.pads.cs_t.o), 1) - result = yield from spi_iface.transfer([0xAA, 0x55, 0x12, 0x34]) - self.assertEqual(result, bytearray([0xAA, 0x55, 0x12, 0x34])) - self.assertEqual((yield mux_iface.pads.cs_t.o), 1) diff --git a/software/glasgow/applet/interface/spi_controller/test.py b/software/glasgow/applet/interface/spi_controller/test.py new file mode 100644 index 000000000..b1af60697 --- /dev/null +++ b/software/glasgow/applet/interface/spi_controller/test.py @@ -0,0 +1,33 @@ +import types +from amaranth import * + +from ... import * +from . import SPIControllerApplet + + +class SPIControllerAppletTestCase(GlasgowAppletTestCase, applet=SPIControllerApplet): + @synthesis_test + def test_build(self): + self.assertBuilds(args=["--pin-sck", "0", "--pin-cs", "1", + "--pin-copi", "2", "--pin-cipo", "3"]) + + def setup_loopback(self): + self.build_simulated_applet() + mux_iface = self.applet.mux_interface + m = Module() + m.d.comb += mux_iface.pads.cipo_t.i.eq(mux_iface.pads.copi_t.o) + self.target.add_submodule(m) + + @applet_simulation_test("setup_loopback", + ["--pin-sck", "0", "--pin-cs", "1", + "--pin-copi", "2", "--pin-cipo", "3", + "--frequency", "5000"]) + @types.coroutine + def test_loopback(self): + mux_iface = self.applet.mux_interface + spi_iface = yield from self.run_simulated_applet() + + self.assertEqual((yield mux_iface.pads.cs_t.o), 1) + result = yield from spi_iface.transfer([0xAA, 0x55, 0x12, 0x34]) + self.assertEqual(result, bytearray([0xAA, 0x55, 0x12, 0x34])) + self.assertEqual((yield mux_iface.pads.cs_t.o), 1) diff --git a/software/glasgow/applet/interface/uart/__init__.py b/software/glasgow/applet/interface/uart/__init__.py index 3dda1084b..ff4b65a53 100644 --- a/software/glasgow/applet/interface/uart/__init__.py +++ b/software/glasgow/applet/interface/uart/__init__.py @@ -386,23 +386,3 @@ async def interact(self, device, args, uart): await self._interact_pty(uart) if args.operation == "socket": await self._interact_socket(uart, args.endpoint) - -# ------------------------------------------------------------------------------------------------- - -class UARTAppletTestCase(GlasgowAppletTestCase, applet=UARTApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() - - def setup_loopback(self): - self.build_simulated_applet() - mux_iface = self.applet.mux_interface - m = Module() - m.d.comb += mux_iface.pads.rx_t.i.eq(mux_iface.pads.tx_t.o) - self.target.add_submodule(m) - - @applet_simulation_test("setup_loopback", ["--baud", "5000000"]) - async def test_loopback(self): - uart_iface = await self.run_simulated_applet() - await uart_iface.write(bytes([0xAA, 0x55])) - self.assertEqual(await uart_iface.read(2), bytes([0xAA, 0x55])) diff --git a/software/glasgow/applet/interface/uart/test.py b/software/glasgow/applet/interface/uart/test.py new file mode 100644 index 000000000..afc475faa --- /dev/null +++ b/software/glasgow/applet/interface/uart/test.py @@ -0,0 +1,23 @@ +from amaranth import * + +from ... import * +from . import UARTApplet + + +class UARTAppletTestCase(GlasgowAppletTestCase, applet=UARTApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() + + def setup_loopback(self): + self.build_simulated_applet() + mux_iface = self.applet.mux_interface + m = Module() + m.d.comb += mux_iface.pads.rx_t.i.eq(mux_iface.pads.tx_t.o) + self.target.add_submodule(m) + + @applet_simulation_test("setup_loopback", ["--baud", "5000000"]) + async def test_loopback(self): + uart_iface = await self.run_simulated_applet() + await uart_iface.write(bytes([0xAA, 0x55])) + self.assertEqual(await uart_iface.read(2), bytes([0xAA, 0x55])) diff --git a/software/glasgow/applet/internal/benchmark/__init__.py b/software/glasgow/applet/internal/benchmark/__init__.py index 02c3e4148..de1890004 100644 --- a/software/glasgow/applet/internal/benchmark/__init__.py +++ b/software/glasgow/applet/internal/benchmark/__init__.py @@ -237,10 +237,3 @@ async def counter(): mode, (length / (end - begin)) / (1 << 20), (length / (end - begin)) / (1 << 17)) - -# ------------------------------------------------------------------------------------------------- - -class BenchmarkAppletTestCase(GlasgowAppletTestCase, applet=BenchmarkApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/internal/benchmark/test.py b/software/glasgow/applet/internal/benchmark/test.py new file mode 100644 index 000000000..1c1cf97b1 --- /dev/null +++ b/software/glasgow/applet/internal/benchmark/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import BenchmarkApplet + + +class BenchmarkAppletTestCase(GlasgowAppletTestCase, applet=BenchmarkApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/internal/selftest/__init__.py b/software/glasgow/applet/internal/selftest/__init__.py index deff0cdc9..75a41b9b6 100644 --- a/software/glasgow/applet/internal/selftest/__init__.py +++ b/software/glasgow/applet/internal/selftest/__init__.py @@ -293,10 +293,3 @@ def decode_pins(bits): for (mode, message) in report: self.logger.error("%s: %s", mode, message) raise GlasgowAppletError("self-test: FAIL") - -# ------------------------------------------------------------------------------------------------- - -class SelfTestAppletTestCase(GlasgowAppletTestCase, applet=SelfTestApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/internal/selftest/test.py b/software/glasgow/applet/internal/selftest/test.py new file mode 100644 index 000000000..fa858cfe6 --- /dev/null +++ b/software/glasgow/applet/internal/selftest/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import SelfTestApplet + + +class SelfTestAppletTestCase(GlasgowAppletTestCase, applet=SelfTestApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/memory/_25x/__init__.py b/software/glasgow/applet/memory/_25x/__init__.py index 186a13989..e58bd0e9c 100644 --- a/software/glasgow/applet/memory/_25x/__init__.py +++ b/software/glasgow/applet/memory/_25x/__init__.py @@ -516,134 +516,3 @@ async def interact(self, device, args, m25x_iface): status = (status & ~MSK_PROT) | ((args.bits << 2) & MSK_PROT) await m25x_iface.write_enable() await m25x_iface.write_status(status) - -# ------------------------------------------------------------------------------------------------- - -import unittest - - -class Memory25xAppletTestCase(GlasgowAppletTestCase, applet=Memory25xApplet): - @synthesis_test - def test_build(self): - self.assertBuilds(args=["--pin-sck", "0", "--pin-cs", "1", - "--pin-copi", "2", "--pin-cipo", "3"]) - - # Flash used for testing: Winbond 25Q32BVSIG - hardware_args = [ - "--voltage", "3.3", - "--pin-cs", "0", "--pin-cipo", "1", - "--pin-copi", "2", "--pin-sck", "3", - "--pin-hold", "4" - ] - dut_ids = (0xef, 0x15, 0x4016) - dut_page_size = 0x100 - dut_sector_size = 0x1000 - dut_block_size = 0x10000 - - async def setup_flash_data(self, mode): - m25x_iface = await self.run_hardware_applet(mode) - if mode == "record": - await m25x_iface.write_enable() - await m25x_iface.sector_erase(0) - await m25x_iface.write_enable() - await m25x_iface.page_program(0, b"Hello, world!") - await m25x_iface.write_enable() - await m25x_iface.sector_erase(self.dut_sector_size) - await m25x_iface.write_enable() - await m25x_iface.page_program(self.dut_sector_size, b"Some more data") - await m25x_iface.write_enable() - await m25x_iface.sector_erase(self.dut_block_size) - await m25x_iface.write_enable() - await m25x_iface.page_program(self.dut_block_size, b"One block later") - return m25x_iface - - @applet_hardware_test(args=hardware_args) - async def test_api_sleep_wake(self, m25x_iface): - await m25x_iface.wakeup() - await m25x_iface.deep_sleep() - - @applet_hardware_test(args=hardware_args) - async def test_api_device_ids(self, m25x_iface): - self.assertEqual(await m25x_iface.read_device_id(), - (self.dut_ids[1],)) - self.assertEqual(await m25x_iface.read_manufacturer_device_id(), - (self.dut_ids[0], self.dut_ids[1])) - self.assertEqual(await m25x_iface.read_manufacturer_long_device_id(), - (self.dut_ids[0], self.dut_ids[2])) - - @applet_hardware_test(setup="setup_flash_data", args=hardware_args) - async def test_api_read(self, m25x_iface): - self.assertEqual(await m25x_iface.read(0, 13), - b"Hello, world!") - self.assertEqual(await m25x_iface.read(self.dut_sector_size, 14), - b"Some more data") - - @applet_hardware_test(setup="setup_flash_data", args=hardware_args) - async def test_api_fast_read(self, m25x_iface): - self.assertEqual(await m25x_iface.fast_read(0, 13), - b"Hello, world!") - self.assertEqual(await m25x_iface.fast_read(self.dut_sector_size, 14), - b"Some more data") - - @applet_hardware_test(setup="setup_flash_data", args=hardware_args) - async def test_api_sector_erase(self, m25x_iface): - await m25x_iface.write_enable() - await m25x_iface.sector_erase(0) - self.assertEqual(await m25x_iface.read(0, 16), - b"\xff" * 16) - self.assertEqual(await m25x_iface.read(self.dut_sector_size, 14), - b"Some more data") - await m25x_iface.write_enable() - await m25x_iface.sector_erase(self.dut_sector_size) - self.assertEqual(await m25x_iface.read(self.dut_sector_size, 16), - b"\xff" * 16) - - @applet_hardware_test(setup="setup_flash_data", args=hardware_args) - async def test_api_block_erase(self, m25x_iface): - await m25x_iface.write_enable() - await m25x_iface.block_erase(0) - self.assertEqual(await m25x_iface.read(0, 16), - b"\xff" * 16) - self.assertEqual(await m25x_iface.read(self.dut_sector_size, 16), - b"\xff" * 16) - self.assertEqual(await m25x_iface.read(self.dut_block_size, 15), - b"One block later") - await m25x_iface.write_enable() - await m25x_iface.block_erase(self.dut_block_size) - self.assertEqual(await m25x_iface.read(self.dut_block_size, 16), - b"\xff" * 16) - - @applet_hardware_test(setup="setup_flash_data", args=hardware_args) - async def test_api_chip_erase(self, m25x_iface): - await m25x_iface.write_enable() - await m25x_iface.chip_erase() - self.assertEqual(await m25x_iface.read(0, 16), - b"\xff" * 16) - self.assertEqual(await m25x_iface.read(self.dut_sector_size, 16), - b"\xff" * 16) - self.assertEqual(await m25x_iface.read(self.dut_block_size, 16), - b"\xff" * 16) - - @applet_hardware_test(setup="setup_flash_data", args=hardware_args) - async def test_api_page_program(self, m25x_iface): - await m25x_iface.write_enable() - await m25x_iface.page_program(self.dut_page_size * 2, b"test") - self.assertEqual(await m25x_iface.read(self.dut_page_size * 2, 4), - b"test") - - @applet_hardware_test(setup="setup_flash_data", args=hardware_args) - async def test_api_program(self, m25x_iface): - # crosses the page boundary - await m25x_iface.write_enable() - await m25x_iface.program(self.dut_page_size * 2 - 6, b"before/after", page_size=0x100) - self.assertEqual(await m25x_iface.read(self.dut_page_size * 2 - 6, 12), - b"before/after") - - @unittest.skip("seems broken??") - @applet_hardware_test(setup="setup_flash_data", args=hardware_args) - async def test_api_erase_program(self, m25x_iface): - await m25x_iface.write_enable() - await m25x_iface.erase_program(0, b"Bye ", - page_size=0x100, sector_size=self.dut_sector_size) - self.assertEqual(await m25x_iface.read(0, 14), - b"Bye , world!") diff --git a/software/glasgow/applet/memory/_25x/test.py b/software/glasgow/applet/memory/_25x/test.py new file mode 100644 index 000000000..d4d838ff7 --- /dev/null +++ b/software/glasgow/applet/memory/_25x/test.py @@ -0,0 +1,131 @@ +import unittest + +from ... import * +from . import Memory25xApplet + + +class Memory25xAppletTestCase(GlasgowAppletTestCase, applet=Memory25xApplet): + @synthesis_test + def test_build(self): + self.assertBuilds(args=["--pin-sck", "0", "--pin-cs", "1", + "--pin-copi", "2", "--pin-cipo", "3"]) + + # Flash used for testing: Winbond 25Q32BVSIG + hardware_args = [ + "--voltage", "3.3", + "--pin-cs", "0", "--pin-cipo", "1", + "--pin-copi", "2", "--pin-sck", "3", + "--pin-hold", "4" + ] + dut_ids = (0xef, 0x15, 0x4016) + dut_page_size = 0x100 + dut_sector_size = 0x1000 + dut_block_size = 0x10000 + + async def setup_flash_data(self, mode): + m25x_iface = await self.run_hardware_applet(mode) + if mode == "record": + await m25x_iface.write_enable() + await m25x_iface.sector_erase(0) + await m25x_iface.write_enable() + await m25x_iface.page_program(0, b"Hello, world!") + await m25x_iface.write_enable() + await m25x_iface.sector_erase(self.dut_sector_size) + await m25x_iface.write_enable() + await m25x_iface.page_program(self.dut_sector_size, b"Some more data") + await m25x_iface.write_enable() + await m25x_iface.sector_erase(self.dut_block_size) + await m25x_iface.write_enable() + await m25x_iface.page_program(self.dut_block_size, b"One block later") + return m25x_iface + + @applet_hardware_test(args=hardware_args) + async def test_api_sleep_wake(self, m25x_iface): + await m25x_iface.wakeup() + await m25x_iface.deep_sleep() + + @applet_hardware_test(args=hardware_args) + async def test_api_device_ids(self, m25x_iface): + self.assertEqual(await m25x_iface.read_device_id(), + (self.dut_ids[1],)) + self.assertEqual(await m25x_iface.read_manufacturer_device_id(), + (self.dut_ids[0], self.dut_ids[1])) + self.assertEqual(await m25x_iface.read_manufacturer_long_device_id(), + (self.dut_ids[0], self.dut_ids[2])) + + @applet_hardware_test(setup="setup_flash_data", args=hardware_args) + async def test_api_read(self, m25x_iface): + self.assertEqual(await m25x_iface.read(0, 13), + b"Hello, world!") + self.assertEqual(await m25x_iface.read(self.dut_sector_size, 14), + b"Some more data") + + @applet_hardware_test(setup="setup_flash_data", args=hardware_args) + async def test_api_fast_read(self, m25x_iface): + self.assertEqual(await m25x_iface.fast_read(0, 13), + b"Hello, world!") + self.assertEqual(await m25x_iface.fast_read(self.dut_sector_size, 14), + b"Some more data") + + @applet_hardware_test(setup="setup_flash_data", args=hardware_args) + async def test_api_sector_erase(self, m25x_iface): + await m25x_iface.write_enable() + await m25x_iface.sector_erase(0) + self.assertEqual(await m25x_iface.read(0, 16), + b"\xff" * 16) + self.assertEqual(await m25x_iface.read(self.dut_sector_size, 14), + b"Some more data") + await m25x_iface.write_enable() + await m25x_iface.sector_erase(self.dut_sector_size) + self.assertEqual(await m25x_iface.read(self.dut_sector_size, 16), + b"\xff" * 16) + + @applet_hardware_test(setup="setup_flash_data", args=hardware_args) + async def test_api_block_erase(self, m25x_iface): + await m25x_iface.write_enable() + await m25x_iface.block_erase(0) + self.assertEqual(await m25x_iface.read(0, 16), + b"\xff" * 16) + self.assertEqual(await m25x_iface.read(self.dut_sector_size, 16), + b"\xff" * 16) + self.assertEqual(await m25x_iface.read(self.dut_block_size, 15), + b"One block later") + await m25x_iface.write_enable() + await m25x_iface.block_erase(self.dut_block_size) + self.assertEqual(await m25x_iface.read(self.dut_block_size, 16), + b"\xff" * 16) + + @applet_hardware_test(setup="setup_flash_data", args=hardware_args) + async def test_api_chip_erase(self, m25x_iface): + await m25x_iface.write_enable() + await m25x_iface.chip_erase() + self.assertEqual(await m25x_iface.read(0, 16), + b"\xff" * 16) + self.assertEqual(await m25x_iface.read(self.dut_sector_size, 16), + b"\xff" * 16) + self.assertEqual(await m25x_iface.read(self.dut_block_size, 16), + b"\xff" * 16) + + @applet_hardware_test(setup="setup_flash_data", args=hardware_args) + async def test_api_page_program(self, m25x_iface): + await m25x_iface.write_enable() + await m25x_iface.page_program(self.dut_page_size * 2, b"test") + self.assertEqual(await m25x_iface.read(self.dut_page_size * 2, 4), + b"test") + + @applet_hardware_test(setup="setup_flash_data", args=hardware_args) + async def test_api_program(self, m25x_iface): + # crosses the page boundary + await m25x_iface.write_enable() + await m25x_iface.program(self.dut_page_size * 2 - 6, b"before/after", page_size=0x100) + self.assertEqual(await m25x_iface.read(self.dut_page_size * 2 - 6, 12), + b"before/after") + + @unittest.skip("seems broken??") + @applet_hardware_test(setup="setup_flash_data", args=hardware_args) + async def test_api_erase_program(self, m25x_iface): + await m25x_iface.write_enable() + await m25x_iface.erase_program(0, b"Bye ", + page_size=0x100, sector_size=self.dut_sector_size) + self.assertEqual(await m25x_iface.read(0, 14), + b"Bye , world!") diff --git a/software/glasgow/applet/memory/floppy/__init__.py b/software/glasgow/applet/memory/floppy/__init__.py index bb3cdfea5..84541d7e6 100644 --- a/software/glasgow/applet/memory/floppy/__init__.py +++ b/software/glasgow/applet/memory/floppy/__init__.py @@ -1084,10 +1084,3 @@ def iter_mfm_sectors(self, symbstream, *, verbose=False, ignore_data_crc=False): if count == 0: state = "IDLE" - -# ------------------------------------------------------------------------------------------------- - -class MemoryFloppyAppletTestCase(GlasgowAppletTestCase, applet=MemoryFloppyApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/memory/floppy/test.py b/software/glasgow/applet/memory/floppy/test.py new file mode 100644 index 000000000..0be5a2f20 --- /dev/null +++ b/software/glasgow/applet/memory/floppy/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import MemoryFloppyApplet + + +class MemoryFloppyAppletTestCase(GlasgowAppletTestCase, applet=MemoryFloppyApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/memory/onfi/__init__.py b/software/glasgow/applet/memory/onfi/__init__.py index 8619d8ad2..90c7a06f9 100644 --- a/software/glasgow/applet/memory/onfi/__init__.py +++ b/software/glasgow/applet/memory/onfi/__init__.py @@ -751,10 +751,3 @@ async def interact(self, device, args, onfi_iface): row += block_size count -= block_size - -# ------------------------------------------------------------------------------------------------- - -class MemoryONFIAppletTestCase(GlasgowAppletTestCase, applet=MemoryONFIApplet): - @synthesis_test - def test_build(self): - self.assertBuilds(args=["--port", "AB"]) diff --git a/software/glasgow/applet/memory/onfi/test.py b/software/glasgow/applet/memory/onfi/test.py new file mode 100644 index 000000000..0ecb71c61 --- /dev/null +++ b/software/glasgow/applet/memory/onfi/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import MemoryONFIApplet + + +class MemoryONFIAppletTestCase(GlasgowAppletTestCase, applet=MemoryONFIApplet): + @synthesis_test + def test_build(self): + self.assertBuilds(args=["--port", "AB"]) diff --git a/software/glasgow/applet/memory/prom/__init__.py b/software/glasgow/applet/memory/prom/__init__.py index bd741a354..caebd8788 100644 --- a/software/glasgow/applet/memory/prom/__init__.py +++ b/software/glasgow/applet/memory/prom/__init__.py @@ -863,10 +863,3 @@ async def run(self, args): print(f"{voltage:.2f}: |{'1' * rectangle_size:{histogram_size}s}| " f"({len(popcounts)}× {int(mean_popcount)}/{density}, " f"sd {statistics.pstdev(popcounts):.2f})") - -# ------------------------------------------------------------------------------------------------- - -class MemoryPROMAppletTestCase(GlasgowAppletTestCase, applet=MemoryPROMApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/memory/prom/test.py b/software/glasgow/applet/memory/prom/test.py new file mode 100644 index 000000000..dbe1d3f44 --- /dev/null +++ b/software/glasgow/applet/memory/prom/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import MemoryPROMApplet + + +class MemoryPROMAppletTestCase(GlasgowAppletTestCase, applet=MemoryPROMApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/program/avr/spi/__init__.py b/software/glasgow/applet/program/avr/spi/__init__.py index 783b8d23f..68a470bdc 100644 --- a/software/glasgow/applet/program/avr/spi/__init__.py +++ b/software/glasgow/applet/program/avr/spi/__init__.py @@ -256,92 +256,3 @@ async def run(self, device, args): spi_iface = await self.run_lower(ProgramAVRSPIApplet, device, args) avr_iface = ProgramAVRSPIInterface(spi_iface, self.logger, self.__addr_dut_reset) return avr_iface - -# ------------------------------------------------------------------------------------------------- - -from .....database.microchip.avr import * - - -class ProgramAVRSPIAppletTestCase(GlasgowAppletTestCase, applet=ProgramAVRSPIApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() - - # Device used for testing: ATmega32U4 - hardware_args = ["-V", "3.3"] - dut_signature = (0x1e, 0x95, 0x87) - dut_device = devices_by_signature[dut_signature] - dut_gold_fuses = (0b11111111, 0b00011000, 0b11001011) - dut_test_fuses = (0b00000000, 0b11000111, 0b11110100) - - async def setup_programming(self, mode): - avr_iface = await self.run_hardware_applet(mode) - if mode == "record": - await avr_iface.programming_enable() - return avr_iface - - @applet_hardware_test(setup="setup_programming", args=hardware_args) - async def test_api_signature(self, avr_iface): - signature = await avr_iface.read_signature() - self.assertEqual(signature, self.dut_signature) - - @applet_hardware_test(setup="setup_programming", args=hardware_args) - async def test_api_calibration(self, avr_iface): - calibration = await avr_iface.read_calibration_range( - range(self.dut_device.calibration_size)) - # could be anything, really - - @applet_hardware_test(setup="setup_programming", args=hardware_args) - async def test_api_fuses(self, avr_iface): - for index, gold_fuse, test_fuse in \ - zip(range(self.dut_device.fuses_size), self.dut_gold_fuses, self.dut_test_fuses): - # program - await avr_iface.write_fuse(index, test_fuse) - # verify - fuse = await avr_iface.read_fuse(index) - self.assertEqual(fuse, test_fuse) - # revert - await avr_iface.write_fuse(index, gold_fuse) - # verify - fuse = await avr_iface.read_fuse(index) - self.assertEqual(fuse, gold_fuse) - - @applet_hardware_test(setup="setup_programming", args=hardware_args) - async def test_api_lock_bits(self, avr_iface): - # erase - await avr_iface.chip_erase() - # verify - lock_bits = await avr_iface.read_lock_bits() - self.assertEqual(lock_bits, 0xff) - # program - await avr_iface.write_lock_bits(0b11111110) - # verify - lock_bits = await avr_iface.read_lock_bits() - self.assertEqual(lock_bits, 0xfe) - - @applet_hardware_test(setup="setup_programming", args=hardware_args) - async def test_api_program_memory(self, avr_iface): - page = self.dut_device.program_page - # erase - await avr_iface.chip_erase() - # program - await avr_iface.write_program_memory_range( - page // 2, [n for n in range(page)], page) - # verify - data = await avr_iface.read_program_memory_range(range(page * 2)) - self.assertEqual(data, - b"\xff" * (page // 2) + bytes([n for n in range(page)]) + b"\xff" * (page // 2)) - - @applet_hardware_test(setup="setup_programming", args=hardware_args) - async def test_api_eeprom(self, avr_iface): - page = self.dut_device.eeprom_page - # erase - await avr_iface.write_eeprom_range( - 0, b"\xff" * page * 2, page) - # program - await avr_iface.write_eeprom_range( - page // 2, [n for n in range(page)], page) - # verify - data = await avr_iface.read_eeprom_range(range(page * 2)) - self.assertEqual(data, - b"\xff" * (page // 2) + bytes([n for n in range(page)]) + b"\xff" * (page // 2)) diff --git a/software/glasgow/applet/program/avr/spi/test.py b/software/glasgow/applet/program/avr/spi/test.py new file mode 100644 index 000000000..55e013c7c --- /dev/null +++ b/software/glasgow/applet/program/avr/spi/test.py @@ -0,0 +1,88 @@ +from .....database.microchip.avr import * +from .... import * +from . import ProgramAVRSPIApplet + + +class ProgramAVRSPIAppletTestCase(GlasgowAppletTestCase, applet=ProgramAVRSPIApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() + + # Device used for testing: ATmega32U4 + hardware_args = ["-V", "3.3"] + dut_signature = (0x1e, 0x95, 0x87) + dut_device = devices_by_signature[dut_signature] + dut_gold_fuses = (0b11111111, 0b00011000, 0b11001011) + dut_test_fuses = (0b00000000, 0b11000111, 0b11110100) + + async def setup_programming(self, mode): + avr_iface = await self.run_hardware_applet(mode) + if mode == "record": + await avr_iface.programming_enable() + return avr_iface + + @applet_hardware_test(setup="setup_programming", args=hardware_args) + async def test_api_signature(self, avr_iface): + signature = await avr_iface.read_signature() + self.assertEqual(signature, self.dut_signature) + + @applet_hardware_test(setup="setup_programming", args=hardware_args) + async def test_api_calibration(self, avr_iface): + calibration = await avr_iface.read_calibration_range( + range(self.dut_device.calibration_size)) + # could be anything, really + + @applet_hardware_test(setup="setup_programming", args=hardware_args) + async def test_api_fuses(self, avr_iface): + for index, gold_fuse, test_fuse in \ + zip(range(self.dut_device.fuses_size), self.dut_gold_fuses, self.dut_test_fuses): + # program + await avr_iface.write_fuse(index, test_fuse) + # verify + fuse = await avr_iface.read_fuse(index) + self.assertEqual(fuse, test_fuse) + # revert + await avr_iface.write_fuse(index, gold_fuse) + # verify + fuse = await avr_iface.read_fuse(index) + self.assertEqual(fuse, gold_fuse) + + @applet_hardware_test(setup="setup_programming", args=hardware_args) + async def test_api_lock_bits(self, avr_iface): + # erase + await avr_iface.chip_erase() + # verify + lock_bits = await avr_iface.read_lock_bits() + self.assertEqual(lock_bits, 0xff) + # program + await avr_iface.write_lock_bits(0b11111110) + # verify + lock_bits = await avr_iface.read_lock_bits() + self.assertEqual(lock_bits, 0xfe) + + @applet_hardware_test(setup="setup_programming", args=hardware_args) + async def test_api_program_memory(self, avr_iface): + page = self.dut_device.program_page + # erase + await avr_iface.chip_erase() + # program + await avr_iface.write_program_memory_range( + page // 2, [n for n in range(page)], page) + # verify + data = await avr_iface.read_program_memory_range(range(page * 2)) + self.assertEqual(data, + b"\xff" * (page // 2) + bytes([n for n in range(page)]) + b"\xff" * (page // 2)) + + @applet_hardware_test(setup="setup_programming", args=hardware_args) + async def test_api_eeprom(self, avr_iface): + page = self.dut_device.eeprom_page + # erase + await avr_iface.write_eeprom_range( + 0, b"\xff" * page * 2, page) + # program + await avr_iface.write_eeprom_range( + page // 2, [n for n in range(page)], page) + # verify + data = await avr_iface.read_eeprom_range(range(page * 2)) + self.assertEqual(data, + b"\xff" * (page // 2) + bytes([n for n in range(page)]) + b"\xff" * (page // 2)) diff --git a/software/glasgow/applet/program/ice40_flash/__init__.py b/software/glasgow/applet/program/ice40_flash/__init__.py index 7a52d99d2..405a7988d 100644 --- a/software/glasgow/applet/program/ice40_flash/__init__.py +++ b/software/glasgow/applet/program/ice40_flash/__init__.py @@ -109,10 +109,3 @@ async def interact(self, device, args, ice40_iface): self.logger.info("FPGA configured from flash") else: self.logger.warning("FPGA failed to configure after releasing reset") - -# ------------------------------------------------------------------------------------------------- - -class ProgramICE40FlashAppletTestCase(GlasgowAppletTestCase, applet=ProgramICE40FlashApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/program/ice40_flash/test.py b/software/glasgow/applet/program/ice40_flash/test.py new file mode 100644 index 000000000..7d4f38740 --- /dev/null +++ b/software/glasgow/applet/program/ice40_flash/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import ProgramICE40FlashApplet + + +class ProgramICE40FlashAppletTestCase(GlasgowAppletTestCase, applet=ProgramICE40FlashApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/program/ice40_sram/__init__.py b/software/glasgow/applet/program/ice40_sram/__init__.py index f7edcabc4..fc33d49a6 100644 --- a/software/glasgow/applet/program/ice40_sram/__init__.py +++ b/software/glasgow/applet/program/ice40_sram/__init__.py @@ -125,12 +125,3 @@ async def interact(self, device, args, ice40_iface): self.logger.info("FPGA successfully configured") else: self.logger.warning("FPGA failed to configure after releasing reset") - -# ------------------------------------------------------------------------------------------------- - -class ProgramICE40SRAMAppletTestCase(GlasgowAppletTestCase, applet=ProgramICE40SRAMApplet): - @synthesis_test - def test_build(self): - self.assertBuilds(args=["--pin-reset", "0", "--pin-done", "1", - "--pin-sck", "2", "--pin-cs", "3", - "--pin-copi", "4"]) diff --git a/software/glasgow/applet/program/ice40_sram/test.py b/software/glasgow/applet/program/ice40_sram/test.py new file mode 100644 index 000000000..ad83f8113 --- /dev/null +++ b/software/glasgow/applet/program/ice40_sram/test.py @@ -0,0 +1,10 @@ +from ... import * +from . import ProgramICE40SRAMApplet + + +class ProgramICE40SRAMAppletTestCase(GlasgowAppletTestCase, applet=ProgramICE40SRAMApplet): + @synthesis_test + def test_build(self): + self.assertBuilds(args=["--pin-reset", "0", "--pin-done", "1", + "--pin-sck", "2", "--pin-cs", "3", + "--pin-copi", "4"]) diff --git a/software/glasgow/applet/program/m16c/__init__.py b/software/glasgow/applet/program/m16c/__init__.py index 9509784d3..f9904e1b7 100644 --- a/software/glasgow/applet/program/m16c/__init__.py +++ b/software/glasgow/applet/program/m16c/__init__.py @@ -482,10 +482,3 @@ async def interact(self, device, args, iface): finally: await iface.reset_application() - -# ------------------------------------------------------------------------------------------------- - -class ProgramM16CAppletTestCase(GlasgowAppletTestCase, applet=ProgramM16CApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/program/m16c/test.py b/software/glasgow/applet/program/m16c/test.py new file mode 100644 index 000000000..f8d8844ef --- /dev/null +++ b/software/glasgow/applet/program/m16c/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import ProgramM16CApplet + + +class ProgramM16CAppletTestCase(GlasgowAppletTestCase, applet=ProgramM16CApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/program/nrf24lx1/__init__.py b/software/glasgow/applet/program/nrf24lx1/__init__.py index 3f014cefb..baacf0eff 100644 --- a/software/glasgow/applet/program/nrf24lx1/__init__.py +++ b/software/glasgow/applet/program/nrf24lx1/__init__.py @@ -420,10 +420,3 @@ async def check_read_protected(): finally: await nrf24lx1_iface.reset_application() - -# ------------------------------------------------------------------------------------------------- - -class ProgramNRF24Lx1AppletTestCase(GlasgowAppletTestCase, applet=ProgramNRF24Lx1Applet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/program/nrf24lx1/test.py b/software/glasgow/applet/program/nrf24lx1/test.py new file mode 100644 index 000000000..8ad64b0e0 --- /dev/null +++ b/software/glasgow/applet/program/nrf24lx1/test.py @@ -0,0 +1,9 @@ +from ... import * +from . import ProgramNRF24Lx1Applet + +# ------------------------------------------------------------------------------------------------- + +class ProgramNRF24Lx1AppletTestCase(GlasgowAppletTestCase, applet=ProgramNRF24Lx1Applet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/radio/nrf24l01/__init__.py b/software/glasgow/applet/radio/nrf24l01/__init__.py index af64e2eb9..9c82d193e 100644 --- a/software/glasgow/applet/radio/nrf24l01/__init__.py +++ b/software/glasgow/applet/radio/nrf24l01/__init__.py @@ -576,10 +576,3 @@ async def interact(self, device, args, nrf24l01_iface): break finally: await nrf24l01_iface.disable() - -# ------------------------------------------------------------------------------------------------- - -class RadioNRF24L01AppletTestCase(GlasgowAppletTestCase, applet=RadioNRF24L01Applet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/radio/nrf24l01/test.py b/software/glasgow/applet/radio/nrf24l01/test.py new file mode 100644 index 000000000..748b086d0 --- /dev/null +++ b/software/glasgow/applet/radio/nrf24l01/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import RadioNRF24L01Applet + + +class RadioNRF24L01AppletTestCase(GlasgowAppletTestCase, applet=RadioNRF24L01Applet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/sensor/hx711/__init__.py b/software/glasgow/applet/sensor/hx711/__init__.py index 59b3aa3ea..2062e532d 100644 --- a/software/glasgow/applet/sensor/hx711/__init__.py +++ b/software/glasgow/applet/sensor/hx711/__init__.py @@ -211,10 +211,3 @@ async def interact(self, device, args, hx711): while True: sample = await hx711.sample() await data_logger.report_data(fields={"n": sample}) - -# ------------------------------------------------------------------------------------------------- - -class SensorHX711AppletTestCase(GlasgowAppletTestCase, applet=SensorHX711Applet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/sensor/hx711/test.py b/software/glasgow/applet/sensor/hx711/test.py new file mode 100644 index 000000000..629f5af36 --- /dev/null +++ b/software/glasgow/applet/sensor/hx711/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import SensorHX711Applet + + +class SensorHX711AppletTestCase(GlasgowAppletTestCase, applet=SensorHX711Applet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/sensor/pmsx003/__init__.py b/software/glasgow/applet/sensor/pmsx003/__init__.py index c3a028f5d..9653a2aa4 100644 --- a/software/glasgow/applet/sensor/pmsx003/__init__.py +++ b/software/glasgow/applet/sensor/pmsx003/__init__.py @@ -161,10 +161,3 @@ async def interact(self, device, args, pmsx003): await data_logger.report_data(fields) except PMSx003Error as error: await data_logger.report_error(str(error), exception=error) - -# ------------------------------------------------------------------------------------------------- - -class PMSx003AppletTestCase(GlasgowAppletTestCase, applet=SensorPMSx003Applet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/sensor/pmsx003/test.py b/software/glasgow/applet/sensor/pmsx003/test.py new file mode 100644 index 000000000..487c69eb7 --- /dev/null +++ b/software/glasgow/applet/sensor/pmsx003/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import SensorPMSx003Applet + + +class PMSx003AppletTestCase(GlasgowAppletTestCase, applet=SensorPMSx003Applet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/video/hub75_output/__init__.py b/software/glasgow/applet/video/hub75_output/__init__.py index 671379267..ac07362f1 100644 --- a/software/glasgow/applet/video/hub75_output/__init__.py +++ b/software/glasgow/applet/video/hub75_output/__init__.py @@ -162,10 +162,3 @@ def build(self, target, args): async def run(self, device, args): return await device.demultiplexer.claim_interface(self, self.mux_interface, args) - -# ------------------------------------------------------------------------------------------------- - -class VideoHub75OutputAppletTestCase(GlasgowAppletTestCase, applet=VideoHub75OutputApplet): - @synthesis_test - def test_build(self): - self.assertBuilds() diff --git a/software/glasgow/applet/video/hub75_output/test.py b/software/glasgow/applet/video/hub75_output/test.py new file mode 100644 index 000000000..7de8bad9b --- /dev/null +++ b/software/glasgow/applet/video/hub75_output/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import VideoHub75OutputApplet + + +class VideoHub75OutputAppletTestCase(GlasgowAppletTestCase, applet=VideoHub75OutputApplet): + @synthesis_test + def test_build(self): + self.assertBuilds() diff --git a/software/glasgow/applet/video/rgb_input/__init__.py b/software/glasgow/applet/video/rgb_input/__init__.py index c42a0374e..9d3664599 100644 --- a/software/glasgow/applet/video/rgb_input/__init__.py +++ b/software/glasgow/applet/video/rgb_input/__init__.py @@ -174,12 +174,3 @@ async def run(self, device, args): row = ((sync & 0x01) << 7) | (await iface.read(1))[0] print("frame {} row {}".format(frame, row)) - -# ------------------------------------------------------------------------------------------------- - -class VideoRGBInputAppletTestCase(GlasgowAppletTestCase, applet=VideoRGBInputApplet): - @synthesis_test - def test_build(self): - self.assertBuilds(args=["--pins-r", "0:4", "--pins-g", "5:9", "--pins-b", "10:14", - "--pin-dck", "15", "--columns", "160", "--rows", "144", - "--vblank", "960e-6"]) diff --git a/software/glasgow/applet/video/rgb_input/test.py b/software/glasgow/applet/video/rgb_input/test.py new file mode 100644 index 000000000..7f99704b5 --- /dev/null +++ b/software/glasgow/applet/video/rgb_input/test.py @@ -0,0 +1,10 @@ +from ... import * +from . import VideoRGBInputApplet + + +class VideoRGBInputAppletTestCase(GlasgowAppletTestCase, applet=VideoRGBInputApplet): + @synthesis_test + def test_build(self): + self.assertBuilds(args=["--pins-r", "0:4", "--pins-g", "5:9", "--pins-b", "10:14", + "--pin-dck", "15", "--columns", "160", "--rows", "144", + "--vblank", "960e-6"]) diff --git a/software/glasgow/applet/video/vga_output/__init__.py b/software/glasgow/applet/video/vga_output/__init__.py index cd56d6526..0038c14be 100644 --- a/software/glasgow/applet/video/vga_output/__init__.py +++ b/software/glasgow/applet/video/vga_output/__init__.py @@ -203,10 +203,3 @@ def build(self, target, args, test_pattern=True): async def run(self, device, args): return await device.demultiplexer.claim_interface(self, self.mux_interface, args) - -# ------------------------------------------------------------------------------------------------- - -class VGAOutputAppletTestCase(GlasgowAppletTestCase, applet=VGAOutputApplet): - @synthesis_test - def test_build(self): - self.assertBuilds(args=["--port", "B"]) diff --git a/software/glasgow/applet/video/vga_output/test.py b/software/glasgow/applet/video/vga_output/test.py new file mode 100644 index 000000000..b387d1007 --- /dev/null +++ b/software/glasgow/applet/video/vga_output/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import VGAOutputApplet + + +class VGAOutputAppletTestCase(GlasgowAppletTestCase, applet=VGAOutputApplet): + @synthesis_test + def test_build(self): + self.assertBuilds(args=["--port", "B"]) diff --git a/software/glasgow/applet/video/ws2812_output/__init__.py b/software/glasgow/applet/video/ws2812_output/__init__.py index 0a0e20c54..95183dd6c 100644 --- a/software/glasgow/applet/video/ws2812_output/__init__.py +++ b/software/glasgow/applet/video/ws2812_output/__init__.py @@ -209,10 +209,3 @@ async def interact(self, device, args, leds): await leds.flush(wait=False) except asyncio.CancelledError: pass - -# ------------------------------------------------------------------------------------------------- - -class VideoWS2812OutputAppletTestCase(GlasgowAppletTestCase, applet=VideoWS2812OutputApplet): - @synthesis_test - def test_build(self): - self.assertBuilds(args=["--pins-out", "0:3", "-c", "1024"]) diff --git a/software/glasgow/applet/video/ws2812_output/test.py b/software/glasgow/applet/video/ws2812_output/test.py new file mode 100644 index 000000000..b90334242 --- /dev/null +++ b/software/glasgow/applet/video/ws2812_output/test.py @@ -0,0 +1,8 @@ +from ... import * +from . import VideoWS2812OutputApplet + + +class VideoWS2812OutputAppletTestCase(GlasgowAppletTestCase, applet=VideoWS2812OutputApplet): + @synthesis_test + def test_build(self): + self.assertBuilds(args=["--pins-out", "0:3", "-c", "1024"]) From 6d820d0f522da115ce131dd93212248a71695844 Mon Sep 17 00:00:00 2001 From: Wanda Date: Fri, 20 Oct 2023 14:09:41 +0200 Subject: [PATCH 2/4] software: extract test code to test_*.py files. --- software/glasgow/gateware/analyzer.py | 380 --------------- software/glasgow/gateware/clockgen.py | 30 -- software/glasgow/gateware/i2c.py | 489 -------------------- software/glasgow/gateware/lfsr.py | 30 -- software/glasgow/gateware/registers.py | 140 ------ software/glasgow/gateware/uart.py | 198 -------- software/glasgow/protocol/jtag_svf.py | 378 --------------- software/glasgow/support/bits.py | 181 -------- software/glasgow/support/bitstruct.py | 122 ----- software/glasgow/support/chunked_fifo.py | 77 --- software/glasgow/support/endpoint.py | 107 ----- software/glasgow/support/lazy.py | 42 -- software/tests/__init__.py | 0 software/tests/gateware/__init__.py | 0 software/tests/gateware/test_analyzer.py | 380 +++++++++++++++ software/tests/gateware/test_clockgen.py | 29 ++ software/tests/gateware/test_i2c.py | 488 +++++++++++++++++++ software/tests/gateware/test_lfsr.py | 29 ++ software/tests/gateware/test_registers.py | 140 ++++++ software/tests/gateware/test_uart.py | 197 ++++++++ software/tests/protocol/__init__.py | 0 software/tests/protocol/test_jtag_svf.py | 379 +++++++++++++++ software/tests/support/__init__.py | 0 software/tests/support/test_bits.py | 180 +++++++ software/tests/support/test_bitstruct.py | 122 +++++ software/tests/support/test_chunked_fifo.py | 76 +++ software/tests/support/test_endpoint.py | 108 +++++ software/tests/support/test_lazy.py | 41 ++ 28 files changed, 2169 insertions(+), 2174 deletions(-) create mode 100644 software/tests/__init__.py create mode 100644 software/tests/gateware/__init__.py create mode 100644 software/tests/gateware/test_analyzer.py create mode 100644 software/tests/gateware/test_clockgen.py create mode 100644 software/tests/gateware/test_i2c.py create mode 100644 software/tests/gateware/test_lfsr.py create mode 100644 software/tests/gateware/test_registers.py create mode 100644 software/tests/gateware/test_uart.py create mode 100644 software/tests/protocol/__init__.py create mode 100644 software/tests/protocol/test_jtag_svf.py create mode 100644 software/tests/support/__init__.py create mode 100644 software/tests/support/test_bits.py create mode 100644 software/tests/support/test_bitstruct.py create mode 100644 software/tests/support/test_chunked_fifo.py create mode 100644 software/tests/support/test_endpoint.py create mode 100644 software/tests/support/test_lazy.py diff --git a/software/glasgow/gateware/analyzer.py b/software/glasgow/gateware/analyzer.py index 1194adcfd..a547a0746 100644 --- a/software/glasgow/gateware/analyzer.py +++ b/software/glasgow/gateware/analyzer.py @@ -477,383 +477,3 @@ def flush(self, pending=False): def is_done(self): return self._state in ("DONE", "OVERRUN") - -# ------------------------------------------------------------------------------------------------- - -import unittest - -from . import simulation_test - - -class EventAnalyzerTestbench(Elaboratable): - def __init__(self, **kwargs): - self.fifo = SyncFIFOBuffered(width=8, depth=64) - self.dut = EventAnalyzer(self.fifo, **kwargs) - - def elaborate(self, platform): - m = Module() - - m.submodules.fifo = self.fifo - m.submodules.dut = self.dut - - return m - - def trigger(self, index, data): - yield self.dut.event_sources[index].trigger.eq(1) - if self.dut.event_sources[index].width > 0: - yield self.dut.event_sources[index].data.eq(data) - - def step(self): - yield - for event_source in self.dut.event_sources: - yield event_source.trigger.eq(0) - - def read(self, count, limit=128): - data = [] - cycle = 0 - while len(data) < count: - while not (yield self.fifo.r_rdy) and cycle < limit: - yield - cycle += 1 - if not (yield self.fifo.r_rdy): - raise ValueError("FIFO underflow") - data.append((yield self.fifo.r_data)) - yield self.fifo.r_en.eq(1) - yield - yield self.fifo.r_en.eq(0) - yield - - cycle = 16 - while not (yield self.fifo.r_rdy) and cycle < limit: - yield - cycle += 1 - if (yield self.fifo.r_rdy): - raise ValueError("junk in FIFO: %#04x at %d" % ((yield self.fifo.r_data), count)) - - return data - - -class EventAnalyzerTestCase(unittest.TestCase): - def setUp(self): - self.tb = EventAnalyzerTestbench(event_depth=16) - - def configure(self, tb, sources): - for n, args in enumerate(sources): - if not isinstance(args, tuple): - args = (args,) - tb.dut.add_event_source(str(n), "strobe", *args) - - def assertEmitted(self, tb, data, decoded, flush_pending=True): - self.assertEqual((yield from tb.read(len(data))), data) - - decoder = TraceDecoder(self.tb.dut.event_sources) - decoder.process(data) - self.assertEqual(decoder.flush(flush_pending), decoded) - - @simulation_test(sources=(8,)) - def test_one_8bit_src(self, tb): - yield from tb.trigger(0, 0xaa) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, 0xaa, - ], [ - (2, {"0": 0xaa}), - ]) - - @simulation_test(sources=(8,8)) - def test_two_8bit_src(self, tb): - yield from tb.trigger(0, 0xaa) - yield from tb.trigger(1, 0xbb) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, 0xaa, - REPORT_EVENT|1, 0xbb, - ], [ - (2, {"0": 0xaa, "1": 0xbb}), - ]) - - @simulation_test(sources=(12,)) - def test_one_12bit_src(self, tb): - yield from tb.trigger(0, 0xabc) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, 0x0a, 0xbc, - ], [ - (2, {"0": 0xabc}), - ]) - - @simulation_test(sources=(16,)) - def test_one_16bit_src(self, tb): - yield from tb.trigger(0, 0xabcd) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, 0xab, 0xcd, - ], [ - (2, {"0": 0xabcd}), - ]) - - @simulation_test(sources=(24,)) - def test_one_24bit_src(self, tb): - yield from tb.trigger(0, 0xabcdef) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, 0xab, 0xcd, 0xef - ], [ - (2, {"0": 0xabcdef}), - ]) - - @simulation_test(sources=(32,)) - def test_one_32bit_src(self, tb): - yield from tb.trigger(0, 0xabcdef12) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, 0xab, 0xcd, 0xef, 0x12 - ], [ - (2, {"0": 0xabcdef12}), - ]) - - @simulation_test(sources=(0,)) - def test_one_0bit_src(self, tb): - yield from tb.trigger(0, 0) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, - ], [ - (2, {"0": None}), - ]) - - @simulation_test(sources=(0,0)) - def test_two_0bit_src(self, tb): - yield from tb.trigger(0, 0) - yield from tb.trigger(1, 0) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, - REPORT_EVENT|1, - ], [ - (2, {"0": None, "1": None}), - ]) - - @simulation_test(sources=(0,1)) - def test_0bit_1bit_src(self, tb): - yield from tb.trigger(0, 0) - yield from tb.trigger(1, 1) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, - REPORT_EVENT|1, 0b1 - ], [ - (2, {"0": None, "1": 0b1}), - ]) - - @simulation_test(sources=(1,0)) - def test_1bit_0bit_src(self, tb): - yield from tb.trigger(0, 1) - yield from tb.trigger(1, 0) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, 0b1, - REPORT_EVENT|1, - ], [ - (2, {"0": 0b1, "1": None}), - ]) - - @simulation_test(sources=((3, (("a", 1), ("b", 2))),)) - def test_fields(self, tb): - yield from tb.trigger(0, 0b101) - yield from tb.step() - yield from tb.trigger(0, 0b110) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, 0b101, - REPORT_DELAY|1, - REPORT_EVENT|0, 0b110, - ], [ - (2, {"a-0": 0b1, "b-0": 0b10}), - (3, {"a-0": 0b0, "b-0": 0b11}), - ]) - - @simulation_test(sources=(8,)) - def test_delay(self, tb): - yield - yield - yield from tb.trigger(0, 0xaa) - yield from tb.step() - yield - yield from tb.trigger(0, 0xbb) - yield from tb.step() - yield - yield - yield from self.assertEmitted(tb, [ - REPORT_DELAY|4, - REPORT_EVENT|0, 0xaa, - REPORT_DELAY|2, - REPORT_EVENT|0, 0xbb, - ], [ - (4, {"0": 0xaa}), - (6, {"0": 0xbb}), - ]) - - @simulation_test(sources=(1,)) - @unittest.skip("FIXME: see issue #182") - def test_delay_2_septet(self, tb): - yield tb.dut._delay_timer.eq(0b1_1110000) - yield from tb.trigger(0, 1) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|0b0000001, - REPORT_DELAY|0b1110001, - REPORT_EVENT|0, 0b1 - ], [ - (0b1_1110001, {"0": 0b1}), - ]) - - @simulation_test(sources=(1,)) - @unittest.skip("FIXME: see issue #182") - def test_delay_3_septet(self, tb): - yield tb.dut._delay_timer.eq(0b01_0011000_1100011) - yield from tb.trigger(0, 1) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|0b0000001, - REPORT_DELAY|0b0011000, - REPORT_DELAY|0b1100100, - REPORT_EVENT|0, 0b1 - ], [ - (0b01_0011000_1100100, {"0": 0b1}), - ]) - - @simulation_test(sources=(1,)) - @unittest.skip("FIXME: see issue #182") - def test_delay_max(self, tb): - yield tb.dut._delay_timer.eq(0xfffe) - yield from tb.trigger(0, 1) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|0b0000011, - REPORT_DELAY|0b1111111, - REPORT_DELAY|0b1111111, - REPORT_EVENT|0, 0b1 - ], [ - (0xffff, {"0": 0b1}), - ]) - - @simulation_test(sources=(1,)) - @unittest.skip("FIXME: see issue #182") - def test_delay_overflow(self, tb): - yield tb.dut._delay_timer.eq(0xfffe) - yield - yield from tb.trigger(0, 1) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|0b0000100, - REPORT_DELAY|0b0000000, - REPORT_DELAY|0b0000000, - REPORT_EVENT|0, 0b1 - ], [ - (0x10000, {"0": 0b1}), - ]) - - @simulation_test(sources=(1,)) - @unittest.skip("FIXME: see issue #182") - def test_delay_overflow_p1(self, tb): - yield tb.dut._delay_timer.eq(0xfffe) - yield - yield - yield from tb.trigger(0, 1) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|0b0000100, - REPORT_DELAY|0b0000000, - REPORT_DELAY|0b0000001, - REPORT_EVENT|0, 0b1 - ], [ - (0x10001, {"0": 0b1}), - ]) - - @simulation_test(sources=(1,)) - @unittest.skip("FIXME: see issue #182") - def test_delay_4_septet(self, tb): - for _ in range(64): - yield tb.dut._delay_timer.eq(0xfffe) - yield - - yield from tb.trigger(0, 1) - yield from tb.step() - yield from self.assertEmitted(tb, [ - REPORT_DELAY|0b0000001, - REPORT_DELAY|0b1111111, - REPORT_DELAY|0b1111111, - REPORT_DELAY|0b1000001, - REPORT_EVENT|0, 0b1 - ], [ - (0xffff * 64 + 1, {"0": 0b1}), - ]) - - @simulation_test(sources=(1,)) - def test_done(self, tb): - yield from tb.trigger(0, 1) - yield from tb.step() - yield - yield tb.dut.done.eq(1) - yield from self.assertEmitted(tb, [ - REPORT_DELAY|2, - REPORT_EVENT|0, 0b1, - REPORT_DELAY|2, - REPORT_SPECIAL|SPECIAL_DONE - ], [ - (2, {"0": 0b1}), - (4, {}) - ], flush_pending=False) - - @simulation_test(sources=(1,)) - def test_throttle_hyst(self, tb): - for x in range(16): - yield from tb.trigger(0, 1) - yield from tb.step() - self.assertEqual((yield tb.dut.throttle), 0) - yield from tb.trigger(0, 1) - yield from tb.step() - self.assertEqual((yield tb.dut.throttle), 1) - yield tb.fifo.r_en.eq(1) - for x in range(52): - yield - yield tb.fifo.r_en.eq(0) - yield - self.assertEqual((yield tb.dut.throttle), 0) - - @simulation_test(sources=(1,)) - def test_overrun(self, tb): - for x in range(18): - yield from tb.trigger(0, 1) - yield from tb.step() - self.assertEqual((yield tb.dut.overrun), 0) - yield from tb.trigger(0, 1) - yield from tb.step() - self.assertEqual((yield tb.dut.overrun), 1) - yield tb.fifo.r_en.eq(1) - for x in range(55): - while not (yield tb.fifo.r_rdy): - yield - yield - yield tb.fifo.r_en.eq(0) - yield - yield from self.assertEmitted(tb, [ - REPORT_DELAY|0b0000100, - REPORT_DELAY|0b0000000, - REPORT_DELAY|0b0000000, - REPORT_SPECIAL|SPECIAL_OVERRUN, - ], [ - (0x10000, "overrun"), - ], flush_pending=False) diff --git a/software/glasgow/gateware/clockgen.py b/software/glasgow/gateware/clockgen.py index 85424b08f..ea8a21343 100644 --- a/software/glasgow/gateware/clockgen.py +++ b/software/glasgow/gateware/clockgen.py @@ -157,33 +157,3 @@ def derive(cls, input_hz, output_hz, max_deviation_ppm=None, min_cyc=None, deviation_ppm, duty) return cyc - -# ------------------------------------------------------------------------------------------------- - -import unittest -import re - - -class ClockGenTestCase(unittest.TestCase): - def test_freq_negative(self): - with self.assertRaisesRegex(ValueError, - re.escape("output frequency -0.001 kHz is not positive")): - ClockGen.calculate(input_hz=1e6, output_hz=-1) - - def test_freq_too_high(self): - with self.assertRaisesRegex(ValueError, - re.escape("output frequency 2000.000 kHz is higher than input frequency " - "1000.000 kHz")): - ClockGen.calculate(input_hz=1e6, output_hz=2e6) - - def test_period_too_low(self): - with self.assertRaisesRegex(ValueError, - re.escape("output frequency 500.000 kHz requires a period smaller than 3 cycles " - "at input frequency 1000.000 kHz")): - ClockGen.calculate(input_hz=1e6, output_hz=500e3, min_cyc=3) - - def test_deviation_too_high(self): - with self.assertRaisesRegex(ValueError, - re.escape("output frequency 30000.000 kHz deviates from requested frequency " - "18000.000 kHz by 666666 ppm, which is higher than 50000 ppm")): - ClockGen.calculate(input_hz=30e6, output_hz=18e6, max_deviation_ppm=50000) diff --git a/software/glasgow/gateware/i2c.py b/software/glasgow/gateware/i2c.py index 0426c6d97..7f6e3c6cc 100644 --- a/software/glasgow/gateware/i2c.py +++ b/software/glasgow/gateware/i2c.py @@ -2,7 +2,6 @@ from amaranth import * from amaranth.lib.cdc import FFSynchronizer -from amaranth.lib.io import Pin __all__ = ["I2CInitiator", "I2CTarget"] @@ -433,491 +432,3 @@ def elaborate(self, platform): m.next = "IDLE" return m - -# ------------------------------------------------------------------------------------------------- - -import unittest - -from . import simulation_test - - -class I2CTestbench(Elaboratable): - def __init__(self): - self.scl_t = Pin(width=1, dir='io') - self.sda_t = Pin(width=1, dir='io') - - self.scl_i = self.scl_t.i - self.scl_o = Signal(reset=1) - self.sda_i = self.sda_t.i - self.sda_o = Signal(reset=1) - - self.period_cyc = 16 - - def elaborate(self, platform): - m = Module() - - m.submodules.dut = self.dut - - m.d.comb += [ - self.scl_t.i.eq((self.scl_t.o | ~self.scl_t.oe) & self.scl_o), - self.sda_t.i.eq((self.sda_t.o | ~self.sda_t.oe) & self.sda_o), - ] - - return m - - def dut_state(self): - return self.dut._fsm.decoding[(yield self.dut._fsm.state)] - - def half_period(self): - for _ in range(self.period_cyc // 2): - yield - - def wait_for(self, fn): - for _ in range(self.wait_cyc): - yield - if (yield from fn()): - return True - return False - - -class I2CTestCase(unittest.TestCase): - def assertState(self, tb, state): - self.assertEqual((yield from tb.dut_state()), state) - - def assertCondition(self, tb, fn): - self.assertTrue((yield from self.tb.wait_for(fn))) - - -class I2CInitiatorTestbench(I2CTestbench): - def __init__(self): - super().__init__() - - self.dut = I2CInitiator(pads=self, period_cyc=self.period_cyc) - self.wait_cyc = self.period_cyc * 3 - - def strobe(self, signal): - yield signal.eq(1) - yield - yield signal.eq(0) - yield - - def start(self): - yield from self.strobe(self.dut.start) - - def stop(self): - yield from self.strobe(self.dut.stop) - - def read(self, ack): - yield self.dut.ack_i.eq(ack) - yield from self.strobe(self.dut.read) - - def write(self, data): - yield self.dut.data_i.eq(data) - yield from self.strobe(self.dut.write) - - -class I2CInitiatorTestCase(I2CTestCase): - def setUp(self): - self.tb = I2CInitiatorTestbench() - - @simulation_test - def test_start(self, tb): - yield from tb.start() - yield from self.assertState(tb, "START-SDA-L") - yield from self.assertCondition(tb, lambda: (yield tb.dut.bus.start)) - self.assertEqual((yield tb.dut.busy), 0) - - @simulation_test - def test_repeated_start(self, tb): - yield tb.dut.bus.sda_o.eq(0) - yield - yield - yield from tb.start() - yield from self.assertState(tb, "START-SCL-L") - yield from self.assertCondition(tb, lambda: (yield tb.dut.bus.start)) - self.assertEqual((yield tb.dut.busy), 0) - - def start(self, tb): - yield from tb.start() - yield from self.assertCondition(tb, lambda: (yield tb.dut.bus.start)) - - @simulation_test - def test_stop(self, tb): - yield tb.dut.bus.sda_o.eq(0) - yield - yield - yield from tb.stop() - yield from self.assertState(tb, "STOP-SDA-H") - yield from self.assertCondition(tb, lambda: (yield tb.dut.bus.stop)) - self.assertEqual((yield tb.dut.busy), 0) - - def stop(self, tb): - yield from tb.stop() - yield from self.assertCondition(tb, lambda: (yield tb.dut.bus.stop)) - - def write(self, tb, data, bits, ack): - yield from tb.write(data) - for n, bit in enumerate(bits): - yield - yield - yield from self.assertState(tb, "WRITE-DATA-SCL-L" if n == 0 else "WRITE-DATA-SDA-N") - yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 0) - yield from self.assertState(tb, "WRITE-DATA-SDA-X") - yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 1) - self.assertEqual((yield tb.sda_i), bit) - yield - yield from self.tb.half_period() - yield - yield - yield from self.assertState(tb, "WRITE-ACK-SCL-L") - yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 0) - yield tb.sda_o.eq(not ack) - yield from self.assertState(tb, "WRITE-ACK-SDA-H") - yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 1) - yield tb.sda_o.eq(1) - self.assertEqual((yield tb.dut.busy), 1) - yield from self.tb.half_period() - yield - yield - yield - yield - self.assertEqual((yield tb.dut.busy), 0) - self.assertEqual((yield tb.dut.ack_o), ack) - - @simulation_test - def test_write_ack(self, tb): - yield tb.dut.bus.sda_o.eq(0) - yield - yield - yield from self.write(tb, 0xA5, [1, 0, 1, 0, 0, 1, 0, 1], 1) - - @simulation_test - def test_write_nak(self, tb): - yield tb.dut.bus.sda_o.eq(0) - yield - yield - yield from self.write(tb, 0x5A, [0, 1, 0, 1, 1, 0, 1, 0], 0) - - @simulation_test - def test_write_tx(self, tb): - yield from self.start(tb) - yield from self.write(tb, 0x55, [0, 1, 0, 1, 0, 1, 0, 1], 1) - yield from self.write(tb, 0x33, [0, 0, 1, 1, 0, 0, 1, 1], 0) - yield from self.stop(tb) - yield - yield - self.assertEqual((yield tb.sda_i), 1) - self.assertEqual((yield tb.scl_i), 1) - - def read(self, tb, data, bits, ack): - yield from tb.read(ack) - for n, bit in enumerate(bits): - yield - yield - yield from self.assertState(tb, "READ-DATA-SCL-L" if n == 0 else "READ-DATA-SDA-N") - yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 0) - yield tb.sda_o.eq(bit) - yield from self.assertState(tb, "READ-DATA-SDA-H") - yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 1) - yield - yield tb.sda_o.eq(1) - yield from tb.half_period() - yield - yield - yield from self.assertState(tb, "READ-ACK-SCL-L") - yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 0) - yield from self.assertState(tb, "READ-ACK-SDA-X") - yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 1) - self.assertEqual((yield tb.sda_i), not ack) - self.assertEqual((yield tb.dut.busy), 1) - yield from self.tb.half_period() - yield - yield - yield - yield - self.assertEqual((yield tb.dut.busy), 0) - self.assertEqual((yield tb.dut.data_o), data) - - @simulation_test - def test_read_ack(self, tb): - yield tb.dut.bus.sda_o.eq(0) - yield - yield - yield from self.read(tb, 0xA5, [1, 0, 1, 0, 0, 1, 0, 1], 1) - - @simulation_test - def test_read_nak(self, tb): - yield tb.dut.bus.sda_o.eq(0) - yield - yield - yield from self.read(tb, 0x5A, [0, 1, 0, 1, 1, 0, 1, 0], 0) - - @simulation_test - def test_read_tx(self, tb): - yield from self.start(tb) - yield from self.read(tb, 0x55, [0, 1, 0, 1, 0, 1, 0, 1], 1) - yield from self.read(tb, 0x33, [0, 0, 1, 1, 0, 0, 1, 1], 0) - yield from self.stop(tb) - yield - yield - self.assertEqual((yield tb.sda_i), 1) - self.assertEqual((yield tb.scl_i), 1) - - -class I2CTargetTestbench(I2CTestbench): - def __init__(self): - super().__init__() - - self.dut = I2CTarget(pads=self) - self.wait_cyc = self.period_cyc // 4 - - def start(self): - assert (yield self.scl_i) == 1 - assert (yield self.sda_i) == 1 - yield self.sda_o.eq(0) - yield from self.half_period() - - def rep_start(self): - assert (yield self.scl_i) == 1 - assert (yield self.sda_i) == 0 - yield self.scl_o.eq(0) - yield # tHD;DAT - yield self.sda_o.eq(1) - yield from self.half_period() - yield self.scl_o.eq(1) - yield from self.half_period() - yield from self.start() - - def stop(self): - yield self.scl_o.eq(0) - yield # tHD;DAT - yield self.sda_o.eq(0) - yield from self.half_period() - yield self.scl_o.eq(1) - yield from self.half_period() - yield self.sda_o.eq(1) - yield from self.half_period() - - def write_bit(self, bit): - assert (yield self.scl_i) == 1 - yield self.scl_o.eq(0) - yield # tHD;DAT - yield self.sda_o.eq(bit) - yield from self.half_period() - yield self.scl_o.eq(1) - yield from self.half_period() - yield self.sda_o.eq(1) - - def write_octet(self, octet): - for bit in range(8)[::-1]: - yield from self.write_bit((octet >> bit) & 1) - - def read_bit(self): - yield self.scl_o.eq(0) - yield from self.half_period() - yield self.scl_o.eq(1) - bit = (yield self.sda_i) - yield from self.half_period() - return bit - - def read_octet(self): - octet = 0 - for bit in range(8): - octet = (octet << 1) | (yield from self.read_bit()) - return octet - - -class I2CTargetTestCase(I2CTestCase): - def setUp(self): - self.tb = I2CTargetTestbench() - - def simulationSetUp(self, tb): - yield tb.dut.address.eq(0b0101000) - - @simulation_test - def test_addr_shift(self, tb): - yield tb.dut.address.eq(0b1111111) - yield from self.assertState(tb, "IDLE") - yield from tb.start() - yield from self.assertState(tb, "START") - for _ in range(8): - yield from tb.write_bit(1) - yield from self.assertState(tb, "ADDR-SHIFT") - - @simulation_test - def test_addr_stop(self, tb): - yield from tb.start() - yield from tb.write_bit(0) - yield from tb.write_bit(1) - yield from tb.stop() - yield from self.assertState(tb, "IDLE") - - @simulation_test - def test_addr_nak(self, tb): - yield from tb.start() - yield from tb.write_octet(0b11110001) - self.assertEqual((yield from tb.read_bit()), 1) - yield from self.assertState(tb, "IDLE") - - @simulation_test - def test_addr_r_ack(self, tb): - yield from tb.start() - yield from tb.write_octet(0b01010001) - yield tb.scl_o.eq(0) - yield from self.assertCondition(tb, lambda: (yield tb.dut.start)) - yield - yield from self.assertState(tb, "ADDR-ACK") - self.assertEqual((yield tb.sda_i), 0) - yield tb.scl_o.eq(1) - yield from tb.half_period() - yield from self.assertState(tb, "READ-SHIFT") - - @simulation_test - def test_addr_w_ack(self, tb): - yield from tb.start() - yield from tb.write_octet(0b01010000) - yield tb.scl_o.eq(0) - yield from self.assertCondition(tb, lambda: (yield tb.dut.start)) - yield - yield from self.assertState(tb, "ADDR-ACK") - self.assertEqual((yield tb.sda_i), 0) - yield tb.scl_o.eq(1) - yield from tb.half_period() - yield tb.scl_o.eq(0) - yield from tb.half_period() - yield from self.assertState(tb, "WRITE-SHIFT") - - def start_addr(self, tb, read): - yield from tb.start() - yield from tb.write_octet(0b01010000 | read) - self.assertEqual((yield from tb.read_bit()), 0) - - @simulation_test - def test_write_shift(self, tb): - yield from self.start_addr(tb, read=False) - yield from tb.write_octet(0b10100101) - yield tb.scl_o.eq(0) - yield from self.assertCondition(tb, lambda: (yield tb.dut.write)) - self.assertEqual((yield tb.dut.data_i), 0b10100101) - yield - yield from self.assertState(tb, "WRITE-ACK") - - @simulation_test - def test_read_shift(self, tb): - yield from tb.start() - yield from tb.write_octet(0b01010001) - yield tb.scl_o.eq(0) - yield from tb.half_period() - self.assertEqual((yield tb.sda_i), 0) - # this sequence ensures combinatorial feedback works - yield tb.scl_o.eq(1) - yield - yield - yield tb.dut.data_o.eq(0b10100101) - yield - self.assertEqual((yield tb.dut.read), 1) - yield tb.dut.data_o.eq(0) - yield - self.assertEqual((yield tb.dut.read), 0) - yield from tb.half_period() - yield tb.scl_o.eq(1) - self.assertEqual((yield from tb.read_octet()), 0b10100101) - yield - yield from self.assertState(tb, "READ-ACK") - - @simulation_test - def test_write_stop(self, tb): - yield from self.start_addr(tb, read=False) - yield from tb.write_bit(0) - yield from tb.write_bit(1) - yield from tb.stop() - yield from self.assertState(tb, "IDLE") - - @simulation_test - def test_read_stop(self, tb): - yield tb.dut.data_o.eq(0b11111111) - yield from self.start_addr(tb, read=True) - yield from tb.read_bit() - yield from tb.read_bit() - yield from tb.stop() - yield from self.assertState(tb, "IDLE") - - @simulation_test - def test_write_ack(self, tb): - yield from self.start_addr(tb, read=False) - yield from tb.write_octet(0b10100101) - # this sequence ensures combinatorial feedback works - yield tb.scl_o.eq(0) - yield - yield - yield - yield tb.dut.ack_o.eq(1) - yield - self.assertEqual((yield tb.dut.write), 1) - yield tb.dut.ack_o.eq(0) - yield - self.assertEqual((yield tb.dut.write), 0) - yield from tb.half_period() - yield tb.scl_o.eq(1) - self.assertEqual((yield tb.sda_i), 0) - - @simulation_test - def test_write_nak(self, tb): - yield from self.start_addr(tb, read=False) - yield from tb.write_octet(0b10100101) - self.assertEqual((yield from tb.read_bit()), 1) - - @simulation_test - def test_write_ack_stop(self, tb): - yield from self.start_addr(tb, read=False) - yield from tb.write_octet(0b10100101) - self.assertEqual((yield from tb.read_bit()), 1) - yield tb.scl_o.eq(0) - yield tb.sda_o.eq(0) - yield from tb.half_period() - yield tb.scl_o.eq(1) - yield from tb.half_period() - yield tb.sda_o.eq(1) - yield from self.assertCondition(tb, lambda: (yield tb.dut.stop)) - yield - yield from self.assertState(tb, "IDLE") - - @simulation_test - def test_read_ack(self, tb): - yield tb.dut.data_o.eq(0b10101010) - yield from self.start_addr(tb, read=True) - self.assertEqual((yield from tb.read_octet()), 0b10101010) - yield from tb.write_bit(0) - yield from self.assertState(tb, "READ-SHIFT") - - @simulation_test - def test_read_nak(self, tb): - yield tb.dut.data_o.eq(0b10100101) - yield from self.start_addr(tb, read=True) - self.assertEqual((yield from tb.read_octet()), 0b10100101) - yield from tb.write_bit(0) - yield from self.assertState(tb, "READ-SHIFT") - - @simulation_test - def test_read_nak_stop(self, tb): - yield tb.dut.data_o.eq(0b10100101) - yield from self.start_addr(tb, read=True) - self.assertEqual((yield from tb.read_octet()), 0b10100101) - yield tb.scl_o.eq(0) - yield from tb.half_period() - yield from self.assertState(tb, "READ-ACK") - yield tb.scl_o.eq(1) - yield from self.assertCondition(tb, lambda: (yield tb.dut.stop)) - yield - yield from self.assertState(tb, "IDLE") - - @simulation_test - def test_read_ack_read(self, tb): - yield tb.dut.data_o.eq(0b10100101) - yield from self.start_addr(tb, read=True) - self.assertEqual((yield from tb.read_octet()), 0b10100101) - yield tb.dut.data_o.eq(0b00110011) - yield from tb.write_bit(0) - self.assertEqual((yield from tb.read_octet()), 0b00110011) - yield from tb.write_bit(0) - yield from self.assertState(tb, "READ-SHIFT") diff --git a/software/glasgow/gateware/lfsr.py b/software/glasgow/gateware/lfsr.py index 1d249a097..4c6af4cb2 100644 --- a/software/glasgow/gateware/lfsr.py +++ b/software/glasgow/gateware/lfsr.py @@ -51,33 +51,3 @@ def generate(self): value = ((value << 1) & mask) | feedback if value == self.reset: break - -# ------------------------------------------------------------------------------------------------- - -import unittest - -from . import simulation_test - - -class LFSRTestbench(Elaboratable): - def __init__(self, **kwargs): - self.dut = LinearFeedbackShiftRegister(**kwargs) - - def elaborate(self, platform): - return self.dut - - -class LFSRTestCase(unittest.TestCase): - def setUp(self): - self.tb = LFSRTestbench(degree=16, taps=(16, 14, 13, 11)) - - @simulation_test - def test_generate(self, tb): - soft_values = list(self.tb.dut.generate()) - hard_values = [] - for _ in range(len(soft_values)): - hard_values.append((yield self.tb.dut.value)) - yield - - self.assertEqual(len(soft_values), 65535) - self.assertEqual(hard_values, soft_values) diff --git a/software/glasgow/gateware/registers.py b/software/glasgow/gateware/registers.py index 816db77f8..37c4061a1 100644 --- a/software/glasgow/gateware/registers.py +++ b/software/glasgow/gateware/registers.py @@ -84,143 +84,3 @@ def elaborate(self, platform): m.d.sync += reg_data.eq(reg_data >> 8) return m - -# ------------------------------------------------------------------------------------------------- - -import unittest - -from . import simulation_test -from .i2c import I2CTargetTestbench - - -class I2CRegistersTestbench(Elaboratable): - def __init__(self): - self.i2c = I2CTargetTestbench() - self.dut = I2CRegisters(self.i2c.dut) - self.reg_dummy, self.addr_dummy = self.dut.add_rw(8) - self.reg_rw_8, self.addr_rw_8 = self.dut.add_rw(8) - self.reg_ro_8, self.addr_ro_8 = self.dut.add_ro(8) - self.reg_rw_16, self.addr_rw_16 = self.dut.add_rw(16) - self.reg_ro_16, self.addr_ro_16 = self.dut.add_ro(16) - self.reg_rw_12, self.addr_rw_12 = self.dut.add_rw(12) - self.reg_ro_12, self.addr_ro_12 = self.dut.add_ro(12) - - def elaborate(self, platform): - m = Module() - m.submodules.i2c = self.i2c - m.submodules.dut = self.dut - return m - - -class I2CRegistersTestCase(unittest.TestCase): - def setUp(self): - self.tb = I2CRegistersTestbench() - - def simulationSetUp(self, tb): - yield tb.i2c.dut.address.eq(0b0001000) - - @simulation_test - def test_address_write_ack(self, tb): - yield from tb.i2c.start() - yield from tb.i2c.write_octet(0b00010000) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(self.tb.addr_rw_8) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - - @simulation_test - def test_address_write_nak(self, tb): - yield from tb.i2c.start() - yield from tb.i2c.write_octet(0b00010000) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(10) - self.assertEqual((yield from tb.i2c.read_bit()), 1) - - @simulation_test - def test_data_write_8(self, tb): - yield from tb.i2c.start() - yield from tb.i2c.write_octet(0b00010000) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(self.tb.addr_rw_8) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(0b10100101) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - self.assertEqual((yield tb.dut.regs_r[self.tb.addr_rw_8]), 0b10100101) - self.assertEqual((yield tb.dut.regs_r[self.tb.addr_dummy]), 0b00000000) - yield from tb.i2c.stop() - - @simulation_test - def test_data_read_8(self, tb): - yield (tb.dut.regs_r[self.tb.addr_ro_8].eq(0b10100101)) - yield from tb.i2c.start() - yield from tb.i2c.write_octet(0b00010000) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(self.tb.addr_ro_8) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.rep_start() - yield from tb.i2c.write_octet(0b00010001) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - self.assertEqual((yield from tb.i2c.read_octet()), 0b10100101) - yield from tb.i2c.write_bit(1) - yield from tb.i2c.stop() - - @simulation_test - def test_data_write_16(self, tb): - yield from tb.i2c.start() - yield from tb.i2c.write_octet(0b00010000) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(self.tb.addr_rw_16) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(0b11110000) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(0b10100101) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - self.assertEqual((yield tb.dut.regs_r[self.tb.addr_rw_16]), 0b1111000010100101) - yield from tb.i2c.stop() - - @simulation_test - def test_data_read_16(self, tb): - yield (tb.dut.regs_r[self.tb.addr_ro_16].eq(0b1111000010100101)) - yield from tb.i2c.start() - yield from tb.i2c.write_octet(0b00010000) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(self.tb.addr_ro_16) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.rep_start() - yield from tb.i2c.write_octet(0b00010001) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - self.assertEqual((yield from tb.i2c.read_octet()), 0b10100101) - yield from tb.i2c.write_bit(0) - self.assertEqual((yield from tb.i2c.read_octet()), 0b11110000) - yield from tb.i2c.write_bit(1) - yield from tb.i2c.stop() - - @simulation_test - def test_data_write_12(self, tb): - yield from tb.i2c.start() - yield from tb.i2c.write_octet(0b00010000) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(self.tb.addr_rw_12) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(0b00001110) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(0b10100101) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - self.assertEqual((yield tb.dut.regs_r[self.tb.addr_rw_12]), 0b111010100101) - yield from tb.i2c.stop() - - @simulation_test - def test_data_read_12(self, tb): - yield (tb.dut.regs_r[self.tb.addr_ro_12].eq(0b111010100101)) - yield from tb.i2c.start() - yield from tb.i2c.write_octet(0b00010000) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.write_octet(self.tb.addr_ro_12) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - yield from tb.i2c.rep_start() - yield from tb.i2c.write_octet(0b00010001) - self.assertEqual((yield from tb.i2c.read_bit()), 0) - self.assertEqual((yield from tb.i2c.read_octet()), 0b10100101) - yield from tb.i2c.write_bit(0) - self.assertEqual((yield from tb.i2c.read_octet()), 0b00001110) - yield from tb.i2c.write_bit(1) - yield from tb.i2c.stop() diff --git a/software/glasgow/gateware/uart.py b/software/glasgow/gateware/uart.py index 5e04e83dd..a2ba21b5c 100644 --- a/software/glasgow/gateware/uart.py +++ b/software/glasgow/gateware/uart.py @@ -268,201 +268,3 @@ def calc_parity(sig, kind): m.next = "IDLE" return m - -# ------------------------------------------------------------------------------------------------- - -import unittest -from amaranth.lib.io import Pin - -from . import simulation_test - - -class UARTTestbench(Elaboratable): - def __init__(self): - self.rx_t = Pin(width=1, dir='i') - self.rx_i = self.rx_t.i - - self.tx_t = Pin(width=1, dir='oe') - self.tx_o = self.tx_t.o - - self.bit_cyc = 4 - - self.dut = UART(pads=self, bit_cyc=self.bit_cyc) - - def elaborate(self, platform): - return self.dut - - -class UARTRXTestCase(unittest.TestCase): - def setUp(self): - self.tb = UARTTestbench() - - def cyc(self, tb, err=None): - has_err = False - for _ in range(tb.bit_cyc): - yield - if err is not None: - has_err |= (yield err) - else: - self.assertEqual((yield tb.dut.rx_err), 0) - if err is not None: - self.assertTrue(has_err) - - def bit(self, tb, bit): - yield tb.rx_i.eq(bit) - yield from self.cyc(tb) - - def start(self, tb): - yield from self.bit(tb, 0) - yield # CDC latency - self.assertEqual((yield tb.dut.rx_rdy), 0) - - def data(self, tb, bit): - yield from self.bit(tb, bit) - self.assertEqual((yield tb.dut.rx_rdy), 0) - - def stop(self, tb): - yield from self.bit(tb, 1) - self.assertEqual((yield tb.dut.rx_rdy), 0) - - def byte(self, tb, bits): - yield from self.start(tb) - for bit in bits: - yield from self.data(tb, bit) - yield from self.stop(tb) - - def ack(self, tb, data): - self.assertEqual((yield tb.dut.rx_rdy), 0) - yield - yield - yield - self.assertEqual((yield tb.dut.rx_rdy), 1) - self.assertEqual((yield tb.dut.rx_data), data) - yield tb.dut.rx_ack.eq(1) - yield - yield tb.dut.rx_ack.eq(0) - yield - yield - self.assertEqual((yield tb.dut.rx_rdy), 0) - - @simulation_test - def test_rx_0x55(self, tb): - yield from self.byte(tb, [1, 0, 1, 0, 1, 0, 1, 0]) - yield from self.ack(tb, 0x55) - - @simulation_test - def test_rx_0xC3(self, tb): - yield from self.byte(tb, [1, 1, 0, 0, 0, 0, 1, 1]) - yield from self.ack(tb, 0xC3) - - @simulation_test - def test_rx_0x81(self, tb): - yield from self.byte(tb, [1, 0, 0, 0, 0, 0, 0, 1]) - yield from self.ack(tb, 0x81) - - @simulation_test - def test_rx_0xA5(self, tb): - yield from self.byte(tb, [1, 0, 1, 0, 0, 1, 0, 1]) - yield from self.ack(tb, 0xA5) - - @simulation_test - def test_rx_0xFF(self, tb): - yield from self.byte(tb, [1, 1, 1, 1, 1, 1, 1, 1]) - yield from self.ack(tb, 0xFF) - - @simulation_test - def test_rx_back_to_back(self, tb): - yield from self.byte(tb, [1, 0, 1, 0, 1, 0, 1, 0]) - yield from self.ack(tb, 0x55) - yield from self.byte(tb, [0, 1, 0, 1, 0, 1, 0, 1]) - yield from self.ack(tb, 0xAA) - - @simulation_test - def test_rx_ferr(self, tb): - yield from self.start(tb) - for bit in [1, 1, 1, 1, 1, 1, 1, 1]: - yield from self.data(tb, bit) - yield tb.rx_i.eq(0) - yield # CDC latency - yield from self.cyc(tb, err=tb.dut.rx_ferr) - - @simulation_test - def test_rx_ovf(self, tb): - yield from self.byte(tb, [1, 0, 1, 0, 0, 1, 0, 1]) - yield tb.rx_i.eq(0) - yield # CDC latency - yield - yield - self.assertEqual((yield tb.dut.rx_ovf), 1) - yield from self.cyc(tb) - for bit in [1, 0, 1, 0, 0, 1, 0, 1]: - yield from self.data(tb, bit) - yield from self.stop(tb) - yield from self.ack(tb, 0xA5) - - -class UARTTXTestCase(unittest.TestCase): - def setUp(self): - self.tb = UARTTestbench() - - def half_cyc(self, tb): - for _ in range(tb.bit_cyc // 2): - yield - - def cyc(self, tb): - for _ in range(tb.bit_cyc): - yield - - def bit(self, tb, bit): - yield from self.cyc(tb) - self.assertEqual((yield tb.tx_o), bit) - - def start(self, tb, data): - self.assertEqual((yield tb.tx_o), 1) - self.assertEqual((yield tb.dut.tx_rdy), 1) - yield tb.dut.tx_data.eq(data) - yield tb.dut.tx_ack.eq(1) - yield - yield tb.dut.tx_ack.eq(0) - yield - self.assertEqual((yield tb.dut.tx_rdy), 0) - self.assertEqual((yield tb.tx_o), 0) - yield from self.half_cyc(tb) - self.assertEqual((yield tb.tx_o), 0) - - def data(self, tb, bit): - self.assertEqual((yield tb.dut.tx_rdy), 0) - yield from self.bit(tb, bit) - - def stop(self, tb): - self.assertEqual((yield tb.dut.tx_rdy), 0) - yield from self.bit(tb, 1) - - def byte(self, tb, data, bits): - yield from self.start(tb, data) - for bit in bits: - yield from self.data(tb, bit) - yield from self.stop(tb) - yield - yield - - @simulation_test - def test_tx_0x55(self, tb): - yield from self.byte(tb, 0x55, [1, 0, 1, 0, 1, 0, 1, 0]) - - @simulation_test - def test_tx_0x81(self, tb): - yield from self.byte(tb, 0x81, [1, 0, 0, 0, 0, 0, 0, 1]) - - @simulation_test - def test_tx_0xFF(self, tb): - yield from self.byte(tb, 0xFF, [1, 1, 1, 1, 1, 1, 1, 1]) - - @simulation_test - def test_tx_0x00(self, tb): - yield from self.byte(tb, 0x00, [0, 0, 0, 0, 0, 0, 0, 0]) - - @simulation_test - def test_tx_back_to_back(self, tb): - yield from self.byte(tb, 0xAA, [0, 1, 0, 1, 0, 1, 0, 1]) - yield from self.byte(tb, 0x55, [1, 0, 1, 0, 1, 0, 1, 0]) diff --git a/software/glasgow/protocol/jtag_svf.py b/software/glasgow/protocol/jtag_svf.py index 375454674..0ea834591 100644 --- a/software/glasgow/protocol/jtag_svf.py +++ b/software/glasgow/protocol/jtag_svf.py @@ -511,381 +511,3 @@ def svf_piomap(self, mapping): @abstractmethod def svf_pio(self, vector): """Called when the ``PIO`` command is encountered.""" - -# ------------------------------------------------------------------------------------------------- - -import unittest - - -class SVFLexerTestCase(unittest.TestCase): - def assertLexes(self, source, tokens): - self.lexer = SVFLexer(source) - self.assertEqual(list(self.lexer), tokens) - - def test_eof(self): - self.assertLexes("", []) - - def test_comment(self): - self.assertLexes("!foo", - []) - self.assertLexes("//foo", - []) - self.assertLexes("//foo\n!bar\n", - []) - self.assertLexes("//foo\n!bar\nTRST", - ["TRST"]) - - def test_keyword(self): - self.assertLexes("TRST", - ["TRST"]) - self.assertLexes("TRST OFF;", - ["TRST", "OFF", ";"]) - - def test_integer(self): - self.assertLexes("8", [8]) - self.assertLexes("12", [12]) - - def test_real(self): - self.assertLexes("1E6", [1e6]) - self.assertLexes("1E+6", [1e6]) - self.assertLexes("1E-6", [1e-6]) - self.assertLexes("1.1E6", [1.1e6]) - self.assertLexes("1.1", [1.1]) - - def test_bits(self): - self.assertLexes("(0)", [bits("")]) - self.assertLexes("(1)", [bits("1")]) - self.assertLexes("(F)", [bits("1111")]) - self.assertLexes("(f)", [bits("1111")]) - self.assertLexes("(0F)", [bits("1111")]) - self.assertLexes("(A\n5)", [bits("10100101")]) # Test literals split over two lines - self.assertLexes("(A\n\t5)", [bits("10100101")]) # With potential whitespace - self.assertLexes("(A\n 5)", [bits("10100101")]) - self.assertLexes("(A\r\n5)", [bits("10100101")]) # Support both LF & LFCR - self.assertLexes("(A\r\n\t5)", [bits("10100101")]) - self.assertLexes("(A\r\n 5)", [bits("10100101")]) - self.assertLexes("(FF)", [bits("11111111")]) - self.assertLexes("(1AA)", [bits("110101010")]) - - def test_literal(self): - self.assertLexes("(HHZZL)", [("HHZZL",)]) - self.assertLexes("(IN FOO)", [("IN FOO",)]) - - def test_error(self): - with self.assertRaises(SVFParsingError): - SVFLexer("XXX").next() - - -class SVFMockEventHandler: - def __init__(self): - self.events = [] - - def __getattr__(self, name): - if name.startswith("svf_"): - def svf_event(**kwargs): - self.events.append((name, kwargs)) - return svf_event - else: - return super().__getattr__(name) - - -class SVFParserTestCase(unittest.TestCase): - def setUp(self): - self.maxDiff = None - - def assertParses(self, source, events): - self.handler = SVFMockEventHandler() - self.parser = SVFParser(source, self.handler) - self.parser.parse_file() - self.assertEqual(self.handler.events, events) - - def assertErrors(self, source, error): - with self.assertRaisesRegex(SVFParsingError, r"^{}".format(re.escape(error))): - self.handler = SVFMockEventHandler() - self.parser = SVFParser(source, self.handler) - self.parser.parse_file() - - def test_frequency(self): - self.assertParses("FREQUENCY;", - [("svf_frequency", {"frequency": None})]) - self.assertParses("FREQUENCY 1E6 HZ;", - [("svf_frequency", {"frequency": 1e6})]) - self.assertParses("FREQUENCY 1000 HZ;", - [("svf_frequency", {"frequency": 1000})]) - - self.assertErrors("FREQUENCY 1E6;", - "expected HZ") - - def test_trst(self): - self.assertParses("TRST ON;", - [("svf_trst", {"mode": "ON"})]) - self.assertParses("TRST OFF;", - [("svf_trst", {"mode": "OFF"})]) - self.assertParses("TRST Z;", - [("svf_trst", {"mode": "Z"})]) - self.assertParses("TRST ABSENT;", - [("svf_trst", {"mode": "ABSENT"})]) - - self.assertErrors("TRST HZ;", - "expected TRST mode") - - def test_state(self): - self.assertParses("STATE IDLE;", - [("svf_state", {"state": "IDLE", "path": []})]) - self.assertParses("STATE IRUPDATE IDLE;", - [("svf_state", {"state": "IDLE", "path": ["IRUPDATE"]})]) - self.assertParses("STATE IREXIT2 IRUPDATE IDLE;", - [("svf_state", {"state": "IDLE", "path": ["IREXIT2", "IRUPDATE"]})]) - - self.assertErrors("STATE;", - "at least one state required") - self.assertErrors("STATE IRSHIFT;", - "last state must be a stable state") - self.assertErrors("STATE RESET IRSHIFT;", - "last state must be a stable state") - - def test_endir_enddr(self): - for command, event in [ - ("ENDIR", "svf_endir"), - ("ENDDR", "svf_enddr") - ]: - self.assertParses("{c} IRPAUSE;".format(c=command), - [(event, {"state": "IRPAUSE"})]) - - self.assertErrors("{c} IRSHIFT;".format(c=command), - "expected stable TAP state") - self.assertErrors("{c};".format(c=command), - "expected stable TAP state") - - def test_hir_sir_tir_hdr_sdr_tdr(self): - for command, event in [ - ("HIR", "svf_hir"), - ("SIR", "svf_sir"), - ("TIR", "svf_tir"), - ("HDR", "svf_hdr"), - ("SDR", "svf_sdr"), - ("TDR", "svf_tdr"), - ]: - self.assertParses("{c} 0;".format(c=command), [ - (event, { - "tdi": bits(""), - "smask": bits(""), - "tdo": None, - "mask": bits(""), - }), - ]) - self.assertParses("{c} 8 TDI(a);".format(c=command), [ - (event, { - "tdi": bits("00001010"), - "smask": bits("11111111"), - "tdo": None, - "mask": bits("00000000"), - }), - ]) - self.assertParses("{c} 6 TDI(0a);".format(c=command), [ - (event, { - "tdi": bits("001010"), - "smask": bits("111111"), - "tdo": None, - "mask": bits("000000"), - }), - ]) - self.assertParses("{c} 8 TDI(a); {c} 8;".format(c=command), [ - (event, { - "tdi": bits("00001010"), - "smask": bits("11111111"), - "tdo": None, - "mask": bits("00000000"), - }), - (event, { - "tdi": bits("00001010"), - "smask": bits("11111111"), - "tdo": None, - "mask": bits("00000000"), - }), - ]) - self.assertParses("{c} 8 TDI(a) SMASK(3); {c} 8; {c} 12 TDI(b);" - .format(c=command), [ - (event, { - "tdi": bits("00001010"), - "smask": bits("00000011"), - "tdo": None, - "mask": bits("00000000"), - }), - (event, { - "tdi": bits("00001010"), - "smask": bits("00000011"), - "tdo": None, - "mask": bits("00000000"), - }), - (event, { - "tdi": bits("000000001011"), - "smask": bits("111111111111"), - "tdo": None, - "mask": bits("000000000000"), - }), - ]) - self.assertParses("{c} 8 TDI(0) TDO(a) MASK(3); {c} 8; " - "{c} 8 TDO(1); {c} 12 TDI(0) TDO(b);" - .format(c=command), [ - (event, { - "tdi": bits("00000000"), - "smask": bits("11111111"), - "tdo": bits("00001010"), - "mask": bits("00000011"), - }), - (event, { - "tdi": bits("00000000"), - "smask": bits("11111111"), - "tdo": None, - "mask": bits("00000000"), - }), - (event, { - "tdi": bits("00000000"), - "smask": bits("11111111"), - "tdo": bits("00000001"), - "mask": bits("00000011"), - }), - (event, { - "tdi": bits("000000000000"), - "smask": bits("111111111111"), - "tdo": bits("000000001011"), - "mask": bits("111111111111"), - }), - ]) - - self.assertErrors("{c} 8 TDI(aaa);".format(c=command), - "scan data length 12 exceeds command length 8") - self.assertErrors("{c} 8 TDI(0) TDI(0);".format(c=command), - "parameter TDI specified twice") - self.assertErrors("{c} 8;".format(c=command), - "initial value for parameter TDI required") - self.assertErrors("{c} 8 TDI(aa); {c} 12;".format(c=command), - "parameter TDI needs to be specified again because " - "the length changed") - - def test_runtest(self): - self.assertParses("RUNTEST 20000 TCK;", [ - ("svf_runtest", { - "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", - "min_time": None, "max_time": None, "end_state": "IDLE" - }), - ]) - self.assertParses("RUNTEST 20000 TCK 1E3 SEC;", [ - ("svf_runtest", { - "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", - "min_time": 1e3, "max_time": None, "end_state": "IDLE" - }), - ]) - self.assertParses("RUNTEST 20000 TCK 1E3 SEC MAXIMUM 1E6 SEC;", [ - ("svf_runtest", { - "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", - "min_time": 1e3, "max_time": 1e6, "end_state": "IDLE" - }), - ]) - self.assertParses("RUNTEST 20000 TCK 1E3 SEC MAXIMUM 1E6 SEC ENDSTATE RESET;", [ - ("svf_runtest", { - "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", - "min_time": 1e3, "max_time": 1e6, "end_state": "RESET" - }), - ]) - self.assertParses("RUNTEST 20000 TCK 1E3 SEC MAXIMUM 1E6 SEC ENDSTATE RESET;", [ - ("svf_runtest", { - "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", - "min_time": 1e3, "max_time": 1e6, "end_state": "RESET" - }), - ]) - self.assertParses("RUNTEST 20000 TCK ENDSTATE RESET; RUNTEST 100 TCK;", [ - ("svf_runtest", { - "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", - "min_time": None, "max_time": None, "end_state": "RESET" - }), - ("svf_runtest", { - "run_state": "IDLE", "run_count": 100, "run_clock": "TCK", - "min_time": None, "max_time": None, "end_state": "RESET" - }), - ]) - self.assertParses("RUNTEST RESET 20000 TCK ENDSTATE RESET; RUNTEST IDLE 100 TCK;", [ - ("svf_runtest", { - "run_state": "RESET", "run_count": 20000, "run_clock": "TCK", - "min_time": None, "max_time": None, "end_state": "RESET" - }), - ("svf_runtest", { - "run_state": "IDLE", "run_count": 100, "run_clock": "TCK", - "min_time": None, "max_time": None, "end_state": "IDLE" - }), - ]) - - self.assertParses("RUNTEST 20000 SCK;", [ - ("svf_runtest", { - "run_state": "IDLE", "run_count": 20000, "run_clock": "SCK", - "min_time": None, "max_time": None, "end_state": "IDLE" - }), - ]) - - self.assertParses("RUNTEST 1 SEC;", [ - ("svf_runtest", { - "run_state": "IDLE", "run_count": None, "run_clock": "TCK", - "min_time": 1, "max_time": None, "end_state": "IDLE" - }), - ]) - self.assertParses("RUNTEST 1 SEC MAXIMUM 2 SEC;", [ - ("svf_runtest", { - "run_state": "IDLE", "run_count": None, "run_clock": "TCK", - "min_time": 1, "max_time": 2, "end_state": "IDLE" - }), - ]) - self.assertParses("RUNTEST 200 TCK ENDSTATE RESET; RUNTEST 1 SEC;", [ - ("svf_runtest", { - "run_state": "IDLE", "run_count": 200, "run_clock": "TCK", - "min_time": None, "max_time": None, "end_state": "RESET" - }), - ("svf_runtest", { - "run_state": "IDLE", "run_count": None, "run_clock": "TCK", - "min_time": 1, "max_time": None, "end_state": "RESET" - }), - ]) - - self.assertErrors("RUNTEST;", - "expected number") - self.assertErrors("RUNTEST 2 SEC MAXIMUM 1 SEC;", - "maximum time must be greater than minimum time") - - def test_piomap(self): - self.assertParses("PIOMAP (IN FOO OUT BAR);", - [("svf_piomap", {"mapping": "IN FOO OUT BAR"})]) - - self.assertErrors("PIOMAP;", - "expected data") - - def test_pio(self): - self.assertParses("PIO (LHZX);", - [("svf_pio", {"vector": "LHZX"})]) - - self.assertErrors("PIO;", - "expected data") - - def test_last_command(self): - handler = SVFMockEventHandler() - parser = SVFParser(" TRST OFF; SIR 8 TDI (aa); ", handler) - parser.parse_command() - self.assertEqual(parser.last_command(), " TRST OFF;") - parser.parse_command() - self.assertEqual(parser.last_command(), " SIR 8 TDI (aa);") - -# ------------------------------------------------------------------------------------------------- - -class SVFPrintingEventHandler: - def __getattr__(self, name): - if name.startswith("svf_"): - def svf_event(**kwargs): - print((name, kwargs)) - return svf_event - else: - return super().__getattr__(name) - - -if __name__ == "__main__": - import sys - with open(sys.argv[1]) as f: - SVFParser(f.read(), SVFPrintingEventHandler()).parse_file() diff --git a/software/glasgow/support/bits.py b/software/glasgow/support/bits.py index 6f1be8051..0d38e24a3 100644 --- a/software/glasgow/support/bits.py +++ b/software/glasgow/support/bits.py @@ -200,184 +200,3 @@ def find(self, sub, start=0, end=-1): return pos else: return -1 - -# ------------------------------------------------------------------------------------------------- - -import unittest - - -class BitsTestCase(unittest.TestCase): - def assertBits(self, value, bit_length, bit_value): - self.assertIsInstance(value, bits) - self.assertEqual(value._len_, bit_length) - self.assertEqual(value._int_, bit_value) - - def test_from_int(self): - self.assertBits(bits.from_int(0), 0, 0b0) - self.assertBits(bits.from_int(1), 1, 0b1) - self.assertBits(bits.from_int(2), 2, 0b10) - self.assertBits(bits.from_int(2, 5), 5, 0b00010) - self.assertBits(bits.from_int(0b110, 2), 2, 0b10) - self.assertBits(bits.from_int(-1, 16), 16, 0xffff) - - def test_from_int_wrong(self): - with self.assertRaisesRegex(ValueError, - r"invalid negative input for bits\(\): '-1'"): - bits.from_int(-1) - - def test_from_str(self): - self.assertBits(bits.from_str(""), 0, 0b0) - self.assertBits(bits.from_str("0"), 1, 0b0) - self.assertBits(bits.from_str("010"), 3, 0b010) - self.assertBits(bits.from_str("0 1 011_100"), 8, 0b01011100) - self.assertBits(bits.from_str("+0 1 \t011_100"), 8, 0b01011100) - - def test_from_str_wrong(self): - with self.assertRaisesRegex(ValueError, - r"invalid negative input for bits\(\): '-1'"): - bits.from_str("-1") - with self.assertRaisesRegex(ValueError, - r"invalid literal for int\(\) with base 2: '23'"): - bits.from_str("23") - - def test_from_bytes(self): - self.assertBits(bits.from_bytes(b"\xa5", 8), 8, 0b10100101) - self.assertBits(bits.from_bytes(b"\xa5\x01", 9), 9, 0b110100101) - self.assertBits(bits.from_bytes(b"\xa5\xff", 9), 9, 0b110100101) - - def test_from_iter(self): - self.assertBits(bits.from_iter(iter([])), 0, 0b0) - self.assertBits(bits.from_iter(iter([1,1,0,1,0,0,1])), 7, 0b1001011) - - def test_new(self): - self.assertBits(bits(), 0, 0b0) - self.assertBits(bits(10), 4, 0b1010) - self.assertBits(bits(10, 2), 2, 0b10) - self.assertBits(bits("1001"), 4, 0b1001) - self.assertBits(bits(b"\xa5\x01", 9), 9, 0b110100101) - self.assertBits(bits(bytearray(b"\xa5\x01"), 9), 9, 0b110100101) - self.assertBits(bits(memoryview(b"\xa5\x01"), 9), 9, 0b110100101) - self.assertBits(bits([1,1,0,1,0,0,1]), 7, 0b1001011) - self.assertBits(bits(bits("1001"), 2), 2, 0b01) - some = bits("1001") - self.assertIs(bits(some), some) - - def test_new_wrong(self): - with self.assertRaisesRegex(TypeError, - r"invalid input for bits\(\): cannot convert from float"): - bits(1.0) - with self.assertRaisesRegex(ValueError, - r"invalid input for bits\(\): when converting from str " - r"length must not be provided"): - bits("1010", 5) - with self.assertRaisesRegex(ValueError, - r"invalid input for bits\(\): when converting from bytes " - r"length must be provided"): - bits(b"\xa5") - with self.assertRaisesRegex(ValueError, - r"invalid input for bits\(\): when converting from an iterable " - r"length must not be provided"): - bits([1,0,1,0], 5) - - def test_len(self): - self.assertEqual(len(bits(10)), 4) - - def test_bool(self): - self.assertFalse(bits("")) - self.assertTrue(bits("1")) - self.assertTrue(bits("01")) - self.assertTrue(bits("0")) - self.assertTrue(bits("00")) - - def test_int(self): - self.assertEqual(int(bits("1010")), 0b1010) - - def test_str(self): - self.assertEqual(str(bits("")), "") - self.assertEqual(str(bits("0000")), "0000") - self.assertEqual(str(bits("1010")), "1010") - self.assertEqual(str(bits("01010")), "01010") - - def test_bytes(self): - self.assertEqual(bytes(bits("")), b"") - self.assertEqual(bytes(bits("10100101")), b"\xa5") - self.assertEqual(bytes(bits("110100101")), b"\xa5\x01") - - def test_repr(self): - self.assertEqual(repr(bits("")), r"bits('')") - self.assertEqual(repr(bits("1010")), r"bits('1010')") - - def test_getitem_int(self): - some = bits("10001001011") - self.assertEqual(some[0], 1) - self.assertEqual(some[2], 0) - self.assertEqual(some[5], 0) - self.assertEqual(some[-1], 1) - self.assertEqual(some[-2], 0) - self.assertEqual(some[-5], 1) - - def test_getitem_slice(self): - some = bits("10001001011") - self.assertBits(some[:], 11, 0b10001001011) - self.assertBits(some[2:], 9, 0b100010010) - self.assertBits(some[2:9], 7, 0b0010010) - self.assertBits(some[2:-2], 7, 0b0010010) - self.assertBits(some[3:2], 0, 0b0) - - def test_getitem_wrong(self): - with self.assertRaisesRegex(TypeError, - r"bits indices must be integers or slices, not str"): - bits()["x"] - - def test_iter(self): - some = bits("10001001011") - self.assertEqual(list(some), [1,1,0,1,0,0,1,0,0,0,1]) - - def test_eq(self): - self.assertEqual(bits("1010"), 0b1010) - self.assertEqual(bits("1010"), "1010") - self.assertEqual(bits("1010"), bits("1010")) - self.assertNotEqual(bits("0010"), 0b0010) - self.assertNotEqual(bits("0010"), "010") - self.assertNotEqual(bits("1010"), bits("01010")) - self.assertNotEqual(bits("1010"), None) - - def test_add(self): - self.assertBits(bits("1010") + bits("1110"), 8, 0b11101010) - self.assertBits(bits("1010") + (0,1,1,1), 8, 0b11101010) - self.assertBits((0,1,1,1) + bits("1010"), 8, 0b10101110) - - def test_mul(self): - self.assertBits(bits("1011") * 4, 16, 0b1011101110111011) - self.assertBits(4 * bits("1011"), 16, 0b1011101110111011) - - def test_and(self): - self.assertBits(bits("1010") & bits("1100"), 4, 0b1000) - self.assertBits(bits("1010") & "1100", 4, 0b1000) - self.assertBits((0,1,0,1) & bits("1100"), 4, 0b1000) - - def test_or(self): - self.assertBits(bits("1010") | bits("1100"), 4, 0b1110) - self.assertBits(bits("1010") | "1100", 4, 0b1110) - self.assertBits((0,1,0,1) | bits("1100"), 4, 0b1110) - - def test_xor(self): - self.assertBits(bits("1010") ^ bits("1100"), 4, 0b0110) - self.assertBits(bits("1010") ^ "1100", 4, 0b0110) - self.assertBits((0,1,0,1) ^ bits("1100"), 4, 0b0110) - - def test_reversed(self): - self.assertBits(bits("1010").reversed(), 4, 0b0101) - - def test_find(self): - self.assertEqual(bits("1011").find(bits("11")), 0) - self.assertEqual(bits("1011").find(bits("10")), 2) - self.assertEqual(bits("1011").find(bits("01")), 1) - self.assertEqual(bits("1011").find(bits("00")), -1) - - self.assertEqual(bits("101100101").find(bits("10"), 0), 1) - self.assertEqual(bits("101100101").find(bits("10"), 2), 4) - self.assertEqual(bits("101100101").find(bits("10"), 5), 7) - self.assertEqual(bits("101100101").find(bits("10"), 8), -1) - - self.assertEqual(bits("1011").find(bits((1,0))), 1) diff --git a/software/glasgow/support/bitstruct.py b/software/glasgow/support/bitstruct.py index 803dd6ca7..e9c0c651a 100644 --- a/software/glasgow/support/bitstruct.py +++ b/software/glasgow/support/bitstruct.py @@ -165,125 +165,3 @@ def bitstruct(name, size_bits, fields): cls.__module__ = mod return cls - -# ------------------------------------------------------------------------------------------------- - -import unittest - - -class BitstructTestCase(unittest.TestCase): - def test_definition(self): - bs = bitstruct("bs", 10, [("a", 3), ("b", 5), (None, 2)]) - self.assertEqual(bs.__name__, "bs") - self.assertEqual(bs.__module__, __name__) - x = bs(1, 2) - self.assertEqual(x.a, 1) - self.assertEqual(x.b, 2) - self.assertEqual(bs.bit_length(), 10) - self.assertEqual(x.bit_length(), 10) - - def test_misuse(self): - with self.assertRaises(TypeError): - bitstruct("bs", 10, [("a", 3), ("b", 5)]) - - bs = bitstruct("bs", 10, [("a", 3), ("b", 5), (None, 2)]) - - with self.assertRaises(TypeError): - bs(1, 2, b=3) - - with self.assertRaises(TypeError): - bs(c=3) - - x = bs() - with self.assertRaises(ValueError): - x.a = -1 - with self.assertRaises(ValueError): - x.a = 8 - with self.assertRaises(ValueError): - x.a = bits("1") - with self.assertRaises(ValueError): - x.a = bits("1111") - - with self.assertRaises(ValueError): - bs.from_bytes(bytes(3)) - with self.assertRaises(ValueError): - bs.from_bytes(bytes(1)) - with self.assertRaises(ValueError): - bs.from_bits(bits(0, 9)) - with self.assertRaises(ValueError): - bs.from_bits(bits(0, 11)) - with self.assertRaises(ValueError): - bs.from_int(-1) - with self.assertRaises(ValueError): - bs.from_int(1<<10) - - def test_kwargs(self): - bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) - x = bs(a=1, b=2) - self.assertEqual(x.a, 1) - self.assertEqual(x.b, 2) - - def test_large(self): - bs = bitstruct("bs", 72, [(None, 8), ("a", 64)]) - val = (3 << 62) + 1 - x = bs(val) - self.assertEqual(x.to_int(), val << 8) - - def test_huge(self): - bs = bitstruct("bs", 2080, [("e", 32), ("m", 2048)]) - x = bs(65537, (30<<2048) // 31) - self.assertEqual(x.e, 65537) - self.assertEqual(x.m, (30<<2048) // 31) - - def test_reserved(self): - bs = bitstruct("bs", 64, [(None, 1), ("a", 1), (None, 62)]) - x = bs(1) - self.assertEqual(repr(x), "<%s.bs a=1>" % __name__) - - def test_bytes(self): - bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) - x = bs(1, 2) - self.assertIsInstance(x.to_bytes(), bytes) - self.assertEqual(x.to_bytes(), b"\x11") - self.assertEqual(bs.from_bytes(x.to_bytes()), x) - - def test_bytearray(self): - bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) - x = bs(1, 2) - self.assertIsInstance(x.to_bytearray(), bytearray) - self.assertEqual(x.to_bytearray(), bytearray(b"\x11")) - self.assertEqual(bs.from_bytearray(x.to_bytearray()), x) - - def test_int(self): - bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) - x = bs(1, 2) - self.assertIsInstance(x.to_int(), int) - self.assertEqual(x.to_int(), 17) - self.assertEqual(bs.from_int(x.to_int()), x) - - def test_bits(self): - bs = bitstruct("bs", 10, [("a", 3), ("b", 7)]) - x = bs(1, 2) - self.assertIsInstance(x.to_bits(), bits) - self.assertEqual(x.to_bits(), bits("0000010001")) - self.assertEqual(bs.from_bits(x.to_bits()), x) - - def test_repr(self): - bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) - x = bs(1, 2) - self.assertEqual(repr(x), "<%s.bs a=001 b=00010>" % __name__) - - def test_copy(self): - bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) - x1 = bs(1, 2) - x2 = x1.copy() - self.assertFalse(x1 is x2) - self.assertEqual(x1, x2) - - def test_slots(self): - bs = bitstruct("bs", 8, [("a", 8)]) - x = bs() - with self.assertRaises(AttributeError): - x.b - with self.assertRaises(AttributeError): - x.b = 1 diff --git a/software/glasgow/support/chunked_fifo.py b/software/glasgow/support/chunked_fifo.py index b8ede3fa9..a6488f457 100644 --- a/software/glasgow/support/chunked_fifo.py +++ b/software/glasgow/support/chunked_fifo.py @@ -92,80 +92,3 @@ def total_read_bytes(self): def total_written_bytes(self): """Determine the total amount of bytes written to the FIFO.""" return self._wtotal - -# ------------------------------------------------------------------------------------------------- - -import unittest -from .bits import bits - - -class ChunkedFIFOTestCase(unittest.TestCase): - def setUp(self): - self.fifo = ChunkedFIFO() - - def test_zero_write(self): - self.fifo.write(b"") - with self.assertRaises(IndexError): - self.fifo.read() - - def test_zero_read(self): - self.assertEqual(self.fifo.read(0), b"") - self.fifo.write(b"A") - self.assertEqual(self.fifo.read(0), b"") - self.assertEqual(self.fifo.read(), b"A") - - def test_fast(self): - self.fifo.write(b"AB") - self.fifo.write(b"CD") - self.assertEqual(self.fifo.read(), b"AB") - self.assertEqual(self.fifo.read(), b"CD") - - def test_chunked(self): - self.fifo.write(b"ABCD") - self.fifo.write(b"EF") - self.assertEqual(self.fifo.read(1), b"A") - self.assertEqual(self.fifo.read(1), b"B") - self.assertEqual(self.fifo.read(2), b"CD") - self.assertEqual(self.fifo.read(), b"EF") - - def test_chunked_chunked(self): - self.fifo.write(b"ABCD") - self.fifo.write(b"EF") - self.assertEqual(self.fifo.read(1), b"A") - self.assertEqual(self.fifo.read(1), b"B") - self.assertEqual(self.fifo.read(2), b"CD") - self.assertEqual(self.fifo.read(1), b"E") - self.assertEqual(self.fifo.read(1), b"F") - - def test_chunked_fast(self): - self.fifo.write(b"ABCD") - self.fifo.write(b"EF") - self.assertEqual(self.fifo.read(1), b"A") - self.assertEqual(self.fifo.read(1), b"B") - self.assertEqual(self.fifo.read(), b"CD") - self.assertEqual(self.fifo.read(), b"EF") - - def test_bool(self): - self.assertFalse(self.fifo) - self.fifo.write(b"ABC") - self.assertTrue(self.fifo) - self.fifo.read(1) - self.assertTrue(self.fifo) - self.fifo.read(2) - self.assertFalse(self.fifo) - - def test_len(self): - self.assertEqual(len(self.fifo), 0) - self.fifo.write(b"ABCD") - self.assertEqual(len(self.fifo), 4) - self.fifo.write(b"EF") - self.assertEqual(len(self.fifo), 6) - self.fifo.read(1) - self.assertEqual(len(self.fifo), 5) - self.fifo.read(3) - self.assertEqual(len(self.fifo), 2) - - def test_write_bits(self): - self.fifo.write(bits("1010")) - self.assertEqual(len(self.fifo), 1) - self.assertEqual(self.fifo.read(1), b"\x0a") diff --git a/software/glasgow/support/endpoint.py b/software/glasgow/support/endpoint.py index a2dbfe7cd..f8b58f4fd 100644 --- a/software/glasgow/support/endpoint.py +++ b/software/glasgow/support/endpoint.py @@ -219,110 +219,3 @@ def add_argument(cls, parser, name, default=None): help=help) # FIXME: finish this - -# ------------------------------------------------------------------------------------------------- - -import unittest -import tempfile - - -class EndpointArgumentTestCase(unittest.TestCase): - def test_unix(self): - proto, path = endpoint("unix:/foo/bar") - self.assertEqual(proto, "unix") - self.assertEqual(path, "/foo/bar") - - def test_tcp_localhost(self): - proto, host, port = endpoint("tcp::1234") - self.assertEqual(proto, "tcp") - self.assertEqual(host, "localhost") - self.assertEqual(port, 1234) - - def test_tcp_all(self): - proto, host, port = endpoint("tcp:*:1234") - self.assertEqual(proto, "tcp") - self.assertEqual(host, None) - self.assertEqual(port, 1234) - - def test_tcp_ipv4(self): - proto, host, port = endpoint("tcp:0.0.0.0:1234") - self.assertEqual(proto, "tcp") - self.assertEqual(host, "0.0.0.0") - self.assertEqual(port, 1234) - - def test_tcp_ipv6(self): - proto, host, port = endpoint("tcp:[2001:DB8::1]:1234") - self.assertEqual(proto, "tcp") - self.assertEqual(host, "2001:DB8::1") - self.assertEqual(port, 1234) - - def test_tcp_hostname(self): - proto, host, port = endpoint("tcp:eXample.org:1234") - self.assertEqual(proto, "tcp") - self.assertEqual(host, "eXample.org") - self.assertEqual(port, 1234) - - -class ServerEndpointTestCase(unittest.TestCase): - async def do_test_lifecycle(self): - sock = ("unix", "{}/test_lifecycle_sock".format(tempfile.gettempdir())) - endp = await ServerEndpoint("test_lifecycle", logging.getLogger(__name__), sock) - - conn_rd, conn_wr = await asyncio.open_unix_connection(*sock[1:]) - conn_wr.write(b"ABC") - await conn_wr.drain() - self.assertEqual(await endp.recv(1), b"A") - self.assertEqual(await endp.recv(2), b"BC") - await endp.send(b"XYZ") - self.assertEqual(await conn_rd.readexactly(2), b"XY") - self.assertEqual(await conn_rd.readexactly(1), b"Z") - await endp.close() - self.assertEqual(await conn_rd.read(1), b"") - with self.assertRaises(asyncio.CancelledError): - await endp.recv(1) - conn_wr.close() - - conn_rd, conn_wr = await asyncio.open_unix_connection(*sock[1:]) - conn_wr.write(b"ABC") - await conn_wr.drain() - self.assertEqual(await endp.recv(3), b"ABC") - conn_wr.close() - with self.assertRaises(asyncio.CancelledError): - await endp.recv(1) - - def test_lifecycle(self): - asyncio.get_event_loop().run_until_complete( - self.do_test_lifecycle()) - - async def do_test_until(self): - sock = ("unix", "{}/test_until_sock".format(tempfile.gettempdir())) - endp = await ServerEndpoint("test_until", logging.getLogger(__name__), sock) - - conn_rd, conn_wr = await asyncio.open_unix_connection(*sock[1:]) - conn_wr.write(b"ABC;DEF") - await conn_wr.drain() - self.assertEqual(await endp.recv_until(b";"), b"ABC") - conn_wr.write(b";") - await conn_wr.drain() - self.assertEqual(await endp.recv_until(b";"), b"DEF") - - def test_until(self): - asyncio.get_event_loop().run_until_complete( - self.do_test_until()) - - async def do_test_tcp(self): - sock = ("tcp", "localhost", 9999) - endp = await ServerEndpoint("test_tcp", logging.getLogger(__name__), sock) - - conn_rd, conn_wr = await asyncio.open_connection(*sock[1:]) - conn_wr.write(b"ABC") - await conn_wr.drain() - self.assertEqual(await endp.recv(3), b"ABC") - await endp.send(b"XYZ") - self.assertEqual(await conn_rd.readexactly(3), b"XYZ") - conn_wr.close() - self.assertEqual(await endp.recv(1), None) - - def test_tcp(self): - asyncio.get_event_loop().run_until_complete( - self.do_test_lifecycle()) diff --git a/software/glasgow/support/lazy.py b/software/glasgow/support/lazy.py index 4ae5e39c7..8872ccfe4 100644 --- a/software/glasgow/support/lazy.py +++ b/software/glasgow/support/lazy.py @@ -65,45 +65,3 @@ def forward(self, *args, **kwargs): ] for special in _specials: lazy._define_special_(special) - -# ------------------------------------------------------------------------------------------------- - -import unittest - - -class _test(object): - pass - - -class LazyTestCase(unittest.TestCase): - def test_get(self): - x = _test() - x.f = 0 - y = lazy(lambda: x) - x.f = 1 - self.assertEqual(y.f, 1) - - def test_set(self): - x = _test() - x.f = 0 - y = lazy(lambda: x) - y.f = 1 - self.assertEqual(x.f, 1) - - def test_del(self): - x = _test() - x.f = 0 - y = lazy(lambda: x) - del y.f - with self.assertRaises(AttributeError): - x.f - - def test_str(self): - x = lazy(lambda: "foo") - self.assertEqual(str(x), "foo") - - def test_repr(self): - x = lazy(lambda: "foo") - self.assertTrue(repr(x).startswith(" 0: + yield self.dut.event_sources[index].data.eq(data) + + def step(self): + yield + for event_source in self.dut.event_sources: + yield event_source.trigger.eq(0) + + def read(self, count, limit=128): + data = [] + cycle = 0 + while len(data) < count: + while not (yield self.fifo.r_rdy) and cycle < limit: + yield + cycle += 1 + if not (yield self.fifo.r_rdy): + raise ValueError("FIFO underflow") + data.append((yield self.fifo.r_data)) + yield self.fifo.r_en.eq(1) + yield + yield self.fifo.r_en.eq(0) + yield + + cycle = 16 + while not (yield self.fifo.r_rdy) and cycle < limit: + yield + cycle += 1 + if (yield self.fifo.r_rdy): + raise ValueError("junk in FIFO: %#04x at %d" % ((yield self.fifo.r_data), count)) + + return data + + +class EventAnalyzerTestCase(unittest.TestCase): + def setUp(self): + self.tb = EventAnalyzerTestbench(event_depth=16) + + def configure(self, tb, sources): + for n, args in enumerate(sources): + if not isinstance(args, tuple): + args = (args,) + tb.dut.add_event_source(str(n), "strobe", *args) + + def assertEmitted(self, tb, data, decoded, flush_pending=True): + self.assertEqual((yield from tb.read(len(data))), data) + + decoder = TraceDecoder(self.tb.dut.event_sources) + decoder.process(data) + self.assertEqual(decoder.flush(flush_pending), decoded) + + @simulation_test(sources=(8,)) + def test_one_8bit_src(self, tb): + yield from tb.trigger(0, 0xaa) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, 0xaa, + ], [ + (2, {"0": 0xaa}), + ]) + + @simulation_test(sources=(8,8)) + def test_two_8bit_src(self, tb): + yield from tb.trigger(0, 0xaa) + yield from tb.trigger(1, 0xbb) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, 0xaa, + REPORT_EVENT|1, 0xbb, + ], [ + (2, {"0": 0xaa, "1": 0xbb}), + ]) + + @simulation_test(sources=(12,)) + def test_one_12bit_src(self, tb): + yield from tb.trigger(0, 0xabc) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, 0x0a, 0xbc, + ], [ + (2, {"0": 0xabc}), + ]) + + @simulation_test(sources=(16,)) + def test_one_16bit_src(self, tb): + yield from tb.trigger(0, 0xabcd) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, 0xab, 0xcd, + ], [ + (2, {"0": 0xabcd}), + ]) + + @simulation_test(sources=(24,)) + def test_one_24bit_src(self, tb): + yield from tb.trigger(0, 0xabcdef) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, 0xab, 0xcd, 0xef + ], [ + (2, {"0": 0xabcdef}), + ]) + + @simulation_test(sources=(32,)) + def test_one_32bit_src(self, tb): + yield from tb.trigger(0, 0xabcdef12) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, 0xab, 0xcd, 0xef, 0x12 + ], [ + (2, {"0": 0xabcdef12}), + ]) + + @simulation_test(sources=(0,)) + def test_one_0bit_src(self, tb): + yield from tb.trigger(0, 0) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, + ], [ + (2, {"0": None}), + ]) + + @simulation_test(sources=(0,0)) + def test_two_0bit_src(self, tb): + yield from tb.trigger(0, 0) + yield from tb.trigger(1, 0) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, + REPORT_EVENT|1, + ], [ + (2, {"0": None, "1": None}), + ]) + + @simulation_test(sources=(0,1)) + def test_0bit_1bit_src(self, tb): + yield from tb.trigger(0, 0) + yield from tb.trigger(1, 1) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, + REPORT_EVENT|1, 0b1 + ], [ + (2, {"0": None, "1": 0b1}), + ]) + + @simulation_test(sources=(1,0)) + def test_1bit_0bit_src(self, tb): + yield from tb.trigger(0, 1) + yield from tb.trigger(1, 0) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, 0b1, + REPORT_EVENT|1, + ], [ + (2, {"0": 0b1, "1": None}), + ]) + + @simulation_test(sources=((3, (("a", 1), ("b", 2))),)) + def test_fields(self, tb): + yield from tb.trigger(0, 0b101) + yield from tb.step() + yield from tb.trigger(0, 0b110) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, 0b101, + REPORT_DELAY|1, + REPORT_EVENT|0, 0b110, + ], [ + (2, {"a-0": 0b1, "b-0": 0b10}), + (3, {"a-0": 0b0, "b-0": 0b11}), + ]) + + @simulation_test(sources=(8,)) + def test_delay(self, tb): + yield + yield + yield from tb.trigger(0, 0xaa) + yield from tb.step() + yield + yield from tb.trigger(0, 0xbb) + yield from tb.step() + yield + yield + yield from self.assertEmitted(tb, [ + REPORT_DELAY|4, + REPORT_EVENT|0, 0xaa, + REPORT_DELAY|2, + REPORT_EVENT|0, 0xbb, + ], [ + (4, {"0": 0xaa}), + (6, {"0": 0xbb}), + ]) + + @simulation_test(sources=(1,)) + @unittest.skip("FIXME: see issue #182") + def test_delay_2_septet(self, tb): + yield tb.dut._delay_timer.eq(0b1_1110000) + yield from tb.trigger(0, 1) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|0b0000001, + REPORT_DELAY|0b1110001, + REPORT_EVENT|0, 0b1 + ], [ + (0b1_1110001, {"0": 0b1}), + ]) + + @simulation_test(sources=(1,)) + @unittest.skip("FIXME: see issue #182") + def test_delay_3_septet(self, tb): + yield tb.dut._delay_timer.eq(0b01_0011000_1100011) + yield from tb.trigger(0, 1) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|0b0000001, + REPORT_DELAY|0b0011000, + REPORT_DELAY|0b1100100, + REPORT_EVENT|0, 0b1 + ], [ + (0b01_0011000_1100100, {"0": 0b1}), + ]) + + @simulation_test(sources=(1,)) + @unittest.skip("FIXME: see issue #182") + def test_delay_max(self, tb): + yield tb.dut._delay_timer.eq(0xfffe) + yield from tb.trigger(0, 1) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|0b0000011, + REPORT_DELAY|0b1111111, + REPORT_DELAY|0b1111111, + REPORT_EVENT|0, 0b1 + ], [ + (0xffff, {"0": 0b1}), + ]) + + @simulation_test(sources=(1,)) + @unittest.skip("FIXME: see issue #182") + def test_delay_overflow(self, tb): + yield tb.dut._delay_timer.eq(0xfffe) + yield + yield from tb.trigger(0, 1) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|0b0000100, + REPORT_DELAY|0b0000000, + REPORT_DELAY|0b0000000, + REPORT_EVENT|0, 0b1 + ], [ + (0x10000, {"0": 0b1}), + ]) + + @simulation_test(sources=(1,)) + @unittest.skip("FIXME: see issue #182") + def test_delay_overflow_p1(self, tb): + yield tb.dut._delay_timer.eq(0xfffe) + yield + yield + yield from tb.trigger(0, 1) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|0b0000100, + REPORT_DELAY|0b0000000, + REPORT_DELAY|0b0000001, + REPORT_EVENT|0, 0b1 + ], [ + (0x10001, {"0": 0b1}), + ]) + + @simulation_test(sources=(1,)) + @unittest.skip("FIXME: see issue #182") + def test_delay_4_septet(self, tb): + for _ in range(64): + yield tb.dut._delay_timer.eq(0xfffe) + yield + + yield from tb.trigger(0, 1) + yield from tb.step() + yield from self.assertEmitted(tb, [ + REPORT_DELAY|0b0000001, + REPORT_DELAY|0b1111111, + REPORT_DELAY|0b1111111, + REPORT_DELAY|0b1000001, + REPORT_EVENT|0, 0b1 + ], [ + (0xffff * 64 + 1, {"0": 0b1}), + ]) + + @simulation_test(sources=(1,)) + def test_done(self, tb): + yield from tb.trigger(0, 1) + yield from tb.step() + yield + yield tb.dut.done.eq(1) + yield from self.assertEmitted(tb, [ + REPORT_DELAY|2, + REPORT_EVENT|0, 0b1, + REPORT_DELAY|2, + REPORT_SPECIAL|SPECIAL_DONE + ], [ + (2, {"0": 0b1}), + (4, {}) + ], flush_pending=False) + + @simulation_test(sources=(1,)) + def test_throttle_hyst(self, tb): + for x in range(16): + yield from tb.trigger(0, 1) + yield from tb.step() + self.assertEqual((yield tb.dut.throttle), 0) + yield from tb.trigger(0, 1) + yield from tb.step() + self.assertEqual((yield tb.dut.throttle), 1) + yield tb.fifo.r_en.eq(1) + for x in range(52): + yield + yield tb.fifo.r_en.eq(0) + yield + self.assertEqual((yield tb.dut.throttle), 0) + + @simulation_test(sources=(1,)) + def test_overrun(self, tb): + for x in range(18): + yield from tb.trigger(0, 1) + yield from tb.step() + self.assertEqual((yield tb.dut.overrun), 0) + yield from tb.trigger(0, 1) + yield from tb.step() + self.assertEqual((yield tb.dut.overrun), 1) + yield tb.fifo.r_en.eq(1) + for x in range(55): + while not (yield tb.fifo.r_rdy): + yield + yield + yield tb.fifo.r_en.eq(0) + yield + yield from self.assertEmitted(tb, [ + REPORT_DELAY|0b0000100, + REPORT_DELAY|0b0000000, + REPORT_DELAY|0b0000000, + REPORT_SPECIAL|SPECIAL_OVERRUN, + ], [ + (0x10000, "overrun"), + ], flush_pending=False) diff --git a/software/tests/gateware/test_clockgen.py b/software/tests/gateware/test_clockgen.py new file mode 100644 index 000000000..eba448bb5 --- /dev/null +++ b/software/tests/gateware/test_clockgen.py @@ -0,0 +1,29 @@ +import unittest +import re + +from glasgow.gateware.clockgen import ClockGen + + +class ClockGenTestCase(unittest.TestCase): + def test_freq_negative(self): + with self.assertRaisesRegex(ValueError, + re.escape("output frequency -0.001 kHz is not positive")): + ClockGen.calculate(input_hz=1e6, output_hz=-1) + + def test_freq_too_high(self): + with self.assertRaisesRegex(ValueError, + re.escape("output frequency 2000.000 kHz is higher than input frequency " + "1000.000 kHz")): + ClockGen.calculate(input_hz=1e6, output_hz=2e6) + + def test_period_too_low(self): + with self.assertRaisesRegex(ValueError, + re.escape("output frequency 500.000 kHz requires a period smaller than 3 cycles " + "at input frequency 1000.000 kHz")): + ClockGen.calculate(input_hz=1e6, output_hz=500e3, min_cyc=3) + + def test_deviation_too_high(self): + with self.assertRaisesRegex(ValueError, + re.escape("output frequency 30000.000 kHz deviates from requested frequency " + "18000.000 kHz by 666666 ppm, which is higher than 50000 ppm")): + ClockGen.calculate(input_hz=30e6, output_hz=18e6, max_deviation_ppm=50000) diff --git a/software/tests/gateware/test_i2c.py b/software/tests/gateware/test_i2c.py new file mode 100644 index 000000000..ec7b2870d --- /dev/null +++ b/software/tests/gateware/test_i2c.py @@ -0,0 +1,488 @@ +import unittest +from amaranth import * +from amaranth.lib.io import Pin + +from glasgow.gateware import simulation_test +from glasgow.gateware.i2c import I2CInitiator, I2CTarget + + +class I2CTestbench(Elaboratable): + def __init__(self): + self.scl_t = Pin(width=1, dir='io') + self.sda_t = Pin(width=1, dir='io') + + self.scl_i = self.scl_t.i + self.scl_o = Signal(reset=1) + self.sda_i = self.sda_t.i + self.sda_o = Signal(reset=1) + + self.period_cyc = 16 + + def elaborate(self, platform): + m = Module() + + m.submodules.dut = self.dut + + m.d.comb += [ + self.scl_t.i.eq((self.scl_t.o | ~self.scl_t.oe) & self.scl_o), + self.sda_t.i.eq((self.sda_t.o | ~self.sda_t.oe) & self.sda_o), + ] + + return m + + def dut_state(self): + return self.dut._fsm.decoding[(yield self.dut._fsm.state)] + + def half_period(self): + for _ in range(self.period_cyc // 2): + yield + + def wait_for(self, fn): + for _ in range(self.wait_cyc): + yield + if (yield from fn()): + return True + return False + + +class I2CTestCase(unittest.TestCase): + def assertState(self, tb, state): + self.assertEqual((yield from tb.dut_state()), state) + + def assertCondition(self, tb, fn): + self.assertTrue((yield from self.tb.wait_for(fn))) + + +class I2CInitiatorTestbench(I2CTestbench): + def __init__(self): + super().__init__() + + self.dut = I2CInitiator(pads=self, period_cyc=self.period_cyc) + self.wait_cyc = self.period_cyc * 3 + + def strobe(self, signal): + yield signal.eq(1) + yield + yield signal.eq(0) + yield + + def start(self): + yield from self.strobe(self.dut.start) + + def stop(self): + yield from self.strobe(self.dut.stop) + + def read(self, ack): + yield self.dut.ack_i.eq(ack) + yield from self.strobe(self.dut.read) + + def write(self, data): + yield self.dut.data_i.eq(data) + yield from self.strobe(self.dut.write) + + +class I2CInitiatorTestCase(I2CTestCase): + def setUp(self): + self.tb = I2CInitiatorTestbench() + + @simulation_test + def test_start(self, tb): + yield from tb.start() + yield from self.assertState(tb, "START-SDA-L") + yield from self.assertCondition(tb, lambda: (yield tb.dut.bus.start)) + self.assertEqual((yield tb.dut.busy), 0) + + @simulation_test + def test_repeated_start(self, tb): + yield tb.dut.bus.sda_o.eq(0) + yield + yield + yield from tb.start() + yield from self.assertState(tb, "START-SCL-L") + yield from self.assertCondition(tb, lambda: (yield tb.dut.bus.start)) + self.assertEqual((yield tb.dut.busy), 0) + + def start(self, tb): + yield from tb.start() + yield from self.assertCondition(tb, lambda: (yield tb.dut.bus.start)) + + @simulation_test + def test_stop(self, tb): + yield tb.dut.bus.sda_o.eq(0) + yield + yield + yield from tb.stop() + yield from self.assertState(tb, "STOP-SDA-H") + yield from self.assertCondition(tb, lambda: (yield tb.dut.bus.stop)) + self.assertEqual((yield tb.dut.busy), 0) + + def stop(self, tb): + yield from tb.stop() + yield from self.assertCondition(tb, lambda: (yield tb.dut.bus.stop)) + + def write(self, tb, data, bits, ack): + yield from tb.write(data) + for n, bit in enumerate(bits): + yield + yield + yield from self.assertState(tb, "WRITE-DATA-SCL-L" if n == 0 else "WRITE-DATA-SDA-N") + yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 0) + yield from self.assertState(tb, "WRITE-DATA-SDA-X") + yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 1) + self.assertEqual((yield tb.sda_i), bit) + yield + yield from self.tb.half_period() + yield + yield + yield from self.assertState(tb, "WRITE-ACK-SCL-L") + yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 0) + yield tb.sda_o.eq(not ack) + yield from self.assertState(tb, "WRITE-ACK-SDA-H") + yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 1) + yield tb.sda_o.eq(1) + self.assertEqual((yield tb.dut.busy), 1) + yield from self.tb.half_period() + yield + yield + yield + yield + self.assertEqual((yield tb.dut.busy), 0) + self.assertEqual((yield tb.dut.ack_o), ack) + + @simulation_test + def test_write_ack(self, tb): + yield tb.dut.bus.sda_o.eq(0) + yield + yield + yield from self.write(tb, 0xA5, [1, 0, 1, 0, 0, 1, 0, 1], 1) + + @simulation_test + def test_write_nak(self, tb): + yield tb.dut.bus.sda_o.eq(0) + yield + yield + yield from self.write(tb, 0x5A, [0, 1, 0, 1, 1, 0, 1, 0], 0) + + @simulation_test + def test_write_tx(self, tb): + yield from self.start(tb) + yield from self.write(tb, 0x55, [0, 1, 0, 1, 0, 1, 0, 1], 1) + yield from self.write(tb, 0x33, [0, 0, 1, 1, 0, 0, 1, 1], 0) + yield from self.stop(tb) + yield + yield + self.assertEqual((yield tb.sda_i), 1) + self.assertEqual((yield tb.scl_i), 1) + + def read(self, tb, data, bits, ack): + yield from tb.read(ack) + for n, bit in enumerate(bits): + yield + yield + yield from self.assertState(tb, "READ-DATA-SCL-L" if n == 0 else "READ-DATA-SDA-N") + yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 0) + yield tb.sda_o.eq(bit) + yield from self.assertState(tb, "READ-DATA-SDA-H") + yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 1) + yield + yield tb.sda_o.eq(1) + yield from tb.half_period() + yield + yield + yield from self.assertState(tb, "READ-ACK-SCL-L") + yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 0) + yield from self.assertState(tb, "READ-ACK-SDA-X") + yield from self.assertCondition(tb, lambda: (yield tb.scl_i) == 1) + self.assertEqual((yield tb.sda_i), not ack) + self.assertEqual((yield tb.dut.busy), 1) + yield from self.tb.half_period() + yield + yield + yield + yield + self.assertEqual((yield tb.dut.busy), 0) + self.assertEqual((yield tb.dut.data_o), data) + + @simulation_test + def test_read_ack(self, tb): + yield tb.dut.bus.sda_o.eq(0) + yield + yield + yield from self.read(tb, 0xA5, [1, 0, 1, 0, 0, 1, 0, 1], 1) + + @simulation_test + def test_read_nak(self, tb): + yield tb.dut.bus.sda_o.eq(0) + yield + yield + yield from self.read(tb, 0x5A, [0, 1, 0, 1, 1, 0, 1, 0], 0) + + @simulation_test + def test_read_tx(self, tb): + yield from self.start(tb) + yield from self.read(tb, 0x55, [0, 1, 0, 1, 0, 1, 0, 1], 1) + yield from self.read(tb, 0x33, [0, 0, 1, 1, 0, 0, 1, 1], 0) + yield from self.stop(tb) + yield + yield + self.assertEqual((yield tb.sda_i), 1) + self.assertEqual((yield tb.scl_i), 1) + + +class I2CTargetTestbench(I2CTestbench): + def __init__(self): + super().__init__() + + self.dut = I2CTarget(pads=self) + self.wait_cyc = self.period_cyc // 4 + + def start(self): + assert (yield self.scl_i) == 1 + assert (yield self.sda_i) == 1 + yield self.sda_o.eq(0) + yield from self.half_period() + + def rep_start(self): + assert (yield self.scl_i) == 1 + assert (yield self.sda_i) == 0 + yield self.scl_o.eq(0) + yield # tHD;DAT + yield self.sda_o.eq(1) + yield from self.half_period() + yield self.scl_o.eq(1) + yield from self.half_period() + yield from self.start() + + def stop(self): + yield self.scl_o.eq(0) + yield # tHD;DAT + yield self.sda_o.eq(0) + yield from self.half_period() + yield self.scl_o.eq(1) + yield from self.half_period() + yield self.sda_o.eq(1) + yield from self.half_period() + + def write_bit(self, bit): + assert (yield self.scl_i) == 1 + yield self.scl_o.eq(0) + yield # tHD;DAT + yield self.sda_o.eq(bit) + yield from self.half_period() + yield self.scl_o.eq(1) + yield from self.half_period() + yield self.sda_o.eq(1) + + def write_octet(self, octet): + for bit in range(8)[::-1]: + yield from self.write_bit((octet >> bit) & 1) + + def read_bit(self): + yield self.scl_o.eq(0) + yield from self.half_period() + yield self.scl_o.eq(1) + bit = (yield self.sda_i) + yield from self.half_period() + return bit + + def read_octet(self): + octet = 0 + for bit in range(8): + octet = (octet << 1) | (yield from self.read_bit()) + return octet + + +class I2CTargetTestCase(I2CTestCase): + def setUp(self): + self.tb = I2CTargetTestbench() + + def simulationSetUp(self, tb): + yield tb.dut.address.eq(0b0101000) + + @simulation_test + def test_addr_shift(self, tb): + yield tb.dut.address.eq(0b1111111) + yield from self.assertState(tb, "IDLE") + yield from tb.start() + yield from self.assertState(tb, "START") + for _ in range(8): + yield from tb.write_bit(1) + yield from self.assertState(tb, "ADDR-SHIFT") + + @simulation_test + def test_addr_stop(self, tb): + yield from tb.start() + yield from tb.write_bit(0) + yield from tb.write_bit(1) + yield from tb.stop() + yield from self.assertState(tb, "IDLE") + + @simulation_test + def test_addr_nak(self, tb): + yield from tb.start() + yield from tb.write_octet(0b11110001) + self.assertEqual((yield from tb.read_bit()), 1) + yield from self.assertState(tb, "IDLE") + + @simulation_test + def test_addr_r_ack(self, tb): + yield from tb.start() + yield from tb.write_octet(0b01010001) + yield tb.scl_o.eq(0) + yield from self.assertCondition(tb, lambda: (yield tb.dut.start)) + yield + yield from self.assertState(tb, "ADDR-ACK") + self.assertEqual((yield tb.sda_i), 0) + yield tb.scl_o.eq(1) + yield from tb.half_period() + yield from self.assertState(tb, "READ-SHIFT") + + @simulation_test + def test_addr_w_ack(self, tb): + yield from tb.start() + yield from tb.write_octet(0b01010000) + yield tb.scl_o.eq(0) + yield from self.assertCondition(tb, lambda: (yield tb.dut.start)) + yield + yield from self.assertState(tb, "ADDR-ACK") + self.assertEqual((yield tb.sda_i), 0) + yield tb.scl_o.eq(1) + yield from tb.half_period() + yield tb.scl_o.eq(0) + yield from tb.half_period() + yield from self.assertState(tb, "WRITE-SHIFT") + + def start_addr(self, tb, read): + yield from tb.start() + yield from tb.write_octet(0b01010000 | read) + self.assertEqual((yield from tb.read_bit()), 0) + + @simulation_test + def test_write_shift(self, tb): + yield from self.start_addr(tb, read=False) + yield from tb.write_octet(0b10100101) + yield tb.scl_o.eq(0) + yield from self.assertCondition(tb, lambda: (yield tb.dut.write)) + self.assertEqual((yield tb.dut.data_i), 0b10100101) + yield + yield from self.assertState(tb, "WRITE-ACK") + + @simulation_test + def test_read_shift(self, tb): + yield from tb.start() + yield from tb.write_octet(0b01010001) + yield tb.scl_o.eq(0) + yield from tb.half_period() + self.assertEqual((yield tb.sda_i), 0) + # this sequence ensures combinatorial feedback works + yield tb.scl_o.eq(1) + yield + yield + yield tb.dut.data_o.eq(0b10100101) + yield + self.assertEqual((yield tb.dut.read), 1) + yield tb.dut.data_o.eq(0) + yield + self.assertEqual((yield tb.dut.read), 0) + yield from tb.half_period() + yield tb.scl_o.eq(1) + self.assertEqual((yield from tb.read_octet()), 0b10100101) + yield + yield from self.assertState(tb, "READ-ACK") + + @simulation_test + def test_write_stop(self, tb): + yield from self.start_addr(tb, read=False) + yield from tb.write_bit(0) + yield from tb.write_bit(1) + yield from tb.stop() + yield from self.assertState(tb, "IDLE") + + @simulation_test + def test_read_stop(self, tb): + yield tb.dut.data_o.eq(0b11111111) + yield from self.start_addr(tb, read=True) + yield from tb.read_bit() + yield from tb.read_bit() + yield from tb.stop() + yield from self.assertState(tb, "IDLE") + + @simulation_test + def test_write_ack(self, tb): + yield from self.start_addr(tb, read=False) + yield from tb.write_octet(0b10100101) + # this sequence ensures combinatorial feedback works + yield tb.scl_o.eq(0) + yield + yield + yield + yield tb.dut.ack_o.eq(1) + yield + self.assertEqual((yield tb.dut.write), 1) + yield tb.dut.ack_o.eq(0) + yield + self.assertEqual((yield tb.dut.write), 0) + yield from tb.half_period() + yield tb.scl_o.eq(1) + self.assertEqual((yield tb.sda_i), 0) + + @simulation_test + def test_write_nak(self, tb): + yield from self.start_addr(tb, read=False) + yield from tb.write_octet(0b10100101) + self.assertEqual((yield from tb.read_bit()), 1) + + @simulation_test + def test_write_ack_stop(self, tb): + yield from self.start_addr(tb, read=False) + yield from tb.write_octet(0b10100101) + self.assertEqual((yield from tb.read_bit()), 1) + yield tb.scl_o.eq(0) + yield tb.sda_o.eq(0) + yield from tb.half_period() + yield tb.scl_o.eq(1) + yield from tb.half_period() + yield tb.sda_o.eq(1) + yield from self.assertCondition(tb, lambda: (yield tb.dut.stop)) + yield + yield from self.assertState(tb, "IDLE") + + @simulation_test + def test_read_ack(self, tb): + yield tb.dut.data_o.eq(0b10101010) + yield from self.start_addr(tb, read=True) + self.assertEqual((yield from tb.read_octet()), 0b10101010) + yield from tb.write_bit(0) + yield from self.assertState(tb, "READ-SHIFT") + + @simulation_test + def test_read_nak(self, tb): + yield tb.dut.data_o.eq(0b10100101) + yield from self.start_addr(tb, read=True) + self.assertEqual((yield from tb.read_octet()), 0b10100101) + yield from tb.write_bit(0) + yield from self.assertState(tb, "READ-SHIFT") + + @simulation_test + def test_read_nak_stop(self, tb): + yield tb.dut.data_o.eq(0b10100101) + yield from self.start_addr(tb, read=True) + self.assertEqual((yield from tb.read_octet()), 0b10100101) + yield tb.scl_o.eq(0) + yield from tb.half_period() + yield from self.assertState(tb, "READ-ACK") + yield tb.scl_o.eq(1) + yield from self.assertCondition(tb, lambda: (yield tb.dut.stop)) + yield + yield from self.assertState(tb, "IDLE") + + @simulation_test + def test_read_ack_read(self, tb): + yield tb.dut.data_o.eq(0b10100101) + yield from self.start_addr(tb, read=True) + self.assertEqual((yield from tb.read_octet()), 0b10100101) + yield tb.dut.data_o.eq(0b00110011) + yield from tb.write_bit(0) + self.assertEqual((yield from tb.read_octet()), 0b00110011) + yield from tb.write_bit(0) + yield from self.assertState(tb, "READ-SHIFT") diff --git a/software/tests/gateware/test_lfsr.py b/software/tests/gateware/test_lfsr.py new file mode 100644 index 000000000..c1500ca61 --- /dev/null +++ b/software/tests/gateware/test_lfsr.py @@ -0,0 +1,29 @@ +import unittest +from amaranth import * + +from glasgow.gateware import simulation_test +from glasgow.gateware.lfsr import LinearFeedbackShiftRegister + + +class LFSRTestbench(Elaboratable): + def __init__(self, **kwargs): + self.dut = LinearFeedbackShiftRegister(**kwargs) + + def elaborate(self, platform): + return self.dut + + +class LFSRTestCase(unittest.TestCase): + def setUp(self): + self.tb = LFSRTestbench(degree=16, taps=(16, 14, 13, 11)) + + @simulation_test + def test_generate(self, tb): + soft_values = list(self.tb.dut.generate()) + hard_values = [] + for _ in range(len(soft_values)): + hard_values.append((yield self.tb.dut.value)) + yield + + self.assertEqual(len(soft_values), 65535) + self.assertEqual(hard_values, soft_values) diff --git a/software/tests/gateware/test_registers.py b/software/tests/gateware/test_registers.py new file mode 100644 index 000000000..daacd9a68 --- /dev/null +++ b/software/tests/gateware/test_registers.py @@ -0,0 +1,140 @@ +import unittest +from amaranth import * + +from glasgow.gateware import simulation_test +from glasgow.gateware.registers import I2CRegisters + +from .test_i2c import I2CTargetTestbench + + +class I2CRegistersTestbench(Elaboratable): + def __init__(self): + self.i2c = I2CTargetTestbench() + self.dut = I2CRegisters(self.i2c.dut) + self.reg_dummy, self.addr_dummy = self.dut.add_rw(8) + self.reg_rw_8, self.addr_rw_8 = self.dut.add_rw(8) + self.reg_ro_8, self.addr_ro_8 = self.dut.add_ro(8) + self.reg_rw_16, self.addr_rw_16 = self.dut.add_rw(16) + self.reg_ro_16, self.addr_ro_16 = self.dut.add_ro(16) + self.reg_rw_12, self.addr_rw_12 = self.dut.add_rw(12) + self.reg_ro_12, self.addr_ro_12 = self.dut.add_ro(12) + + def elaborate(self, platform): + m = Module() + m.submodules.i2c = self.i2c + m.submodules.dut = self.dut + return m + + +class I2CRegistersTestCase(unittest.TestCase): + def setUp(self): + self.tb = I2CRegistersTestbench() + + def simulationSetUp(self, tb): + yield tb.i2c.dut.address.eq(0b0001000) + + @simulation_test + def test_address_write_ack(self, tb): + yield from tb.i2c.start() + yield from tb.i2c.write_octet(0b00010000) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(self.tb.addr_rw_8) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + + @simulation_test + def test_address_write_nak(self, tb): + yield from tb.i2c.start() + yield from tb.i2c.write_octet(0b00010000) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(10) + self.assertEqual((yield from tb.i2c.read_bit()), 1) + + @simulation_test + def test_data_write_8(self, tb): + yield from tb.i2c.start() + yield from tb.i2c.write_octet(0b00010000) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(self.tb.addr_rw_8) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(0b10100101) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + self.assertEqual((yield tb.dut.regs_r[self.tb.addr_rw_8]), 0b10100101) + self.assertEqual((yield tb.dut.regs_r[self.tb.addr_dummy]), 0b00000000) + yield from tb.i2c.stop() + + @simulation_test + def test_data_read_8(self, tb): + yield (tb.dut.regs_r[self.tb.addr_ro_8].eq(0b10100101)) + yield from tb.i2c.start() + yield from tb.i2c.write_octet(0b00010000) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(self.tb.addr_ro_8) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.rep_start() + yield from tb.i2c.write_octet(0b00010001) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + self.assertEqual((yield from tb.i2c.read_octet()), 0b10100101) + yield from tb.i2c.write_bit(1) + yield from tb.i2c.stop() + + @simulation_test + def test_data_write_16(self, tb): + yield from tb.i2c.start() + yield from tb.i2c.write_octet(0b00010000) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(self.tb.addr_rw_16) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(0b11110000) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(0b10100101) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + self.assertEqual((yield tb.dut.regs_r[self.tb.addr_rw_16]), 0b1111000010100101) + yield from tb.i2c.stop() + + @simulation_test + def test_data_read_16(self, tb): + yield (tb.dut.regs_r[self.tb.addr_ro_16].eq(0b1111000010100101)) + yield from tb.i2c.start() + yield from tb.i2c.write_octet(0b00010000) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(self.tb.addr_ro_16) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.rep_start() + yield from tb.i2c.write_octet(0b00010001) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + self.assertEqual((yield from tb.i2c.read_octet()), 0b10100101) + yield from tb.i2c.write_bit(0) + self.assertEqual((yield from tb.i2c.read_octet()), 0b11110000) + yield from tb.i2c.write_bit(1) + yield from tb.i2c.stop() + + @simulation_test + def test_data_write_12(self, tb): + yield from tb.i2c.start() + yield from tb.i2c.write_octet(0b00010000) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(self.tb.addr_rw_12) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(0b00001110) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(0b10100101) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + self.assertEqual((yield tb.dut.regs_r[self.tb.addr_rw_12]), 0b111010100101) + yield from tb.i2c.stop() + + @simulation_test + def test_data_read_12(self, tb): + yield (tb.dut.regs_r[self.tb.addr_ro_12].eq(0b111010100101)) + yield from tb.i2c.start() + yield from tb.i2c.write_octet(0b00010000) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.write_octet(self.tb.addr_ro_12) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + yield from tb.i2c.rep_start() + yield from tb.i2c.write_octet(0b00010001) + self.assertEqual((yield from tb.i2c.read_bit()), 0) + self.assertEqual((yield from tb.i2c.read_octet()), 0b10100101) + yield from tb.i2c.write_bit(0) + self.assertEqual((yield from tb.i2c.read_octet()), 0b00001110) + yield from tb.i2c.write_bit(1) + yield from tb.i2c.stop() diff --git a/software/tests/gateware/test_uart.py b/software/tests/gateware/test_uart.py new file mode 100644 index 000000000..a6a5a788a --- /dev/null +++ b/software/tests/gateware/test_uart.py @@ -0,0 +1,197 @@ +import unittest +from amaranth import * +from amaranth.lib.io import Pin + +from glasgow.gateware import simulation_test +from glasgow.gateware.uart import UART + + +class UARTTestbench(Elaboratable): + def __init__(self): + self.rx_t = Pin(width=1, dir='i') + self.rx_i = self.rx_t.i + + self.tx_t = Pin(width=1, dir='oe') + self.tx_o = self.tx_t.o + + self.bit_cyc = 4 + + self.dut = UART(pads=self, bit_cyc=self.bit_cyc) + + def elaborate(self, platform): + return self.dut + + +class UARTRXTestCase(unittest.TestCase): + def setUp(self): + self.tb = UARTTestbench() + + def cyc(self, tb, err=None): + has_err = False + for _ in range(tb.bit_cyc): + yield + if err is not None: + has_err |= (yield err) + else: + self.assertEqual((yield tb.dut.rx_err), 0) + if err is not None: + self.assertTrue(has_err) + + def bit(self, tb, bit): + yield tb.rx_i.eq(bit) + yield from self.cyc(tb) + + def start(self, tb): + yield from self.bit(tb, 0) + yield # CDC latency + self.assertEqual((yield tb.dut.rx_rdy), 0) + + def data(self, tb, bit): + yield from self.bit(tb, bit) + self.assertEqual((yield tb.dut.rx_rdy), 0) + + def stop(self, tb): + yield from self.bit(tb, 1) + self.assertEqual((yield tb.dut.rx_rdy), 0) + + def byte(self, tb, bits): + yield from self.start(tb) + for bit in bits: + yield from self.data(tb, bit) + yield from self.stop(tb) + + def ack(self, tb, data): + self.assertEqual((yield tb.dut.rx_rdy), 0) + yield + yield + yield + self.assertEqual((yield tb.dut.rx_rdy), 1) + self.assertEqual((yield tb.dut.rx_data), data) + yield tb.dut.rx_ack.eq(1) + yield + yield tb.dut.rx_ack.eq(0) + yield + yield + self.assertEqual((yield tb.dut.rx_rdy), 0) + + @simulation_test + def test_rx_0x55(self, tb): + yield from self.byte(tb, [1, 0, 1, 0, 1, 0, 1, 0]) + yield from self.ack(tb, 0x55) + + @simulation_test + def test_rx_0xC3(self, tb): + yield from self.byte(tb, [1, 1, 0, 0, 0, 0, 1, 1]) + yield from self.ack(tb, 0xC3) + + @simulation_test + def test_rx_0x81(self, tb): + yield from self.byte(tb, [1, 0, 0, 0, 0, 0, 0, 1]) + yield from self.ack(tb, 0x81) + + @simulation_test + def test_rx_0xA5(self, tb): + yield from self.byte(tb, [1, 0, 1, 0, 0, 1, 0, 1]) + yield from self.ack(tb, 0xA5) + + @simulation_test + def test_rx_0xFF(self, tb): + yield from self.byte(tb, [1, 1, 1, 1, 1, 1, 1, 1]) + yield from self.ack(tb, 0xFF) + + @simulation_test + def test_rx_back_to_back(self, tb): + yield from self.byte(tb, [1, 0, 1, 0, 1, 0, 1, 0]) + yield from self.ack(tb, 0x55) + yield from self.byte(tb, [0, 1, 0, 1, 0, 1, 0, 1]) + yield from self.ack(tb, 0xAA) + + @simulation_test + def test_rx_ferr(self, tb): + yield from self.start(tb) + for bit in [1, 1, 1, 1, 1, 1, 1, 1]: + yield from self.data(tb, bit) + yield tb.rx_i.eq(0) + yield # CDC latency + yield from self.cyc(tb, err=tb.dut.rx_ferr) + + @simulation_test + def test_rx_ovf(self, tb): + yield from self.byte(tb, [1, 0, 1, 0, 0, 1, 0, 1]) + yield tb.rx_i.eq(0) + yield # CDC latency + yield + yield + self.assertEqual((yield tb.dut.rx_ovf), 1) + yield from self.cyc(tb) + for bit in [1, 0, 1, 0, 0, 1, 0, 1]: + yield from self.data(tb, bit) + yield from self.stop(tb) + yield from self.ack(tb, 0xA5) + + +class UARTTXTestCase(unittest.TestCase): + def setUp(self): + self.tb = UARTTestbench() + + def half_cyc(self, tb): + for _ in range(tb.bit_cyc // 2): + yield + + def cyc(self, tb): + for _ in range(tb.bit_cyc): + yield + + def bit(self, tb, bit): + yield from self.cyc(tb) + self.assertEqual((yield tb.tx_o), bit) + + def start(self, tb, data): + self.assertEqual((yield tb.tx_o), 1) + self.assertEqual((yield tb.dut.tx_rdy), 1) + yield tb.dut.tx_data.eq(data) + yield tb.dut.tx_ack.eq(1) + yield + yield tb.dut.tx_ack.eq(0) + yield + self.assertEqual((yield tb.dut.tx_rdy), 0) + self.assertEqual((yield tb.tx_o), 0) + yield from self.half_cyc(tb) + self.assertEqual((yield tb.tx_o), 0) + + def data(self, tb, bit): + self.assertEqual((yield tb.dut.tx_rdy), 0) + yield from self.bit(tb, bit) + + def stop(self, tb): + self.assertEqual((yield tb.dut.tx_rdy), 0) + yield from self.bit(tb, 1) + + def byte(self, tb, data, bits): + yield from self.start(tb, data) + for bit in bits: + yield from self.data(tb, bit) + yield from self.stop(tb) + yield + yield + + @simulation_test + def test_tx_0x55(self, tb): + yield from self.byte(tb, 0x55, [1, 0, 1, 0, 1, 0, 1, 0]) + + @simulation_test + def test_tx_0x81(self, tb): + yield from self.byte(tb, 0x81, [1, 0, 0, 0, 0, 0, 0, 1]) + + @simulation_test + def test_tx_0xFF(self, tb): + yield from self.byte(tb, 0xFF, [1, 1, 1, 1, 1, 1, 1, 1]) + + @simulation_test + def test_tx_0x00(self, tb): + yield from self.byte(tb, 0x00, [0, 0, 0, 0, 0, 0, 0, 0]) + + @simulation_test + def test_tx_back_to_back(self, tb): + yield from self.byte(tb, 0xAA, [0, 1, 0, 1, 0, 1, 0, 1]) + yield from self.byte(tb, 0x55, [1, 0, 1, 0, 1, 0, 1, 0]) diff --git a/software/tests/protocol/__init__.py b/software/tests/protocol/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/software/tests/protocol/test_jtag_svf.py b/software/tests/protocol/test_jtag_svf.py new file mode 100644 index 000000000..94ade1c56 --- /dev/null +++ b/software/tests/protocol/test_jtag_svf.py @@ -0,0 +1,379 @@ +import re +import unittest + +from glasgow.support.bits import * +from glasgow.protocol.jtag_svf import SVFLexer, SVFParser, SVFParsingError + + +class SVFLexerTestCase(unittest.TestCase): + def assertLexes(self, source, tokens): + self.lexer = SVFLexer(source) + self.assertEqual(list(self.lexer), tokens) + + def test_eof(self): + self.assertLexes("", []) + + def test_comment(self): + self.assertLexes("!foo", + []) + self.assertLexes("//foo", + []) + self.assertLexes("//foo\n!bar\n", + []) + self.assertLexes("//foo\n!bar\nTRST", + ["TRST"]) + + def test_keyword(self): + self.assertLexes("TRST", + ["TRST"]) + self.assertLexes("TRST OFF;", + ["TRST", "OFF", ";"]) + + def test_integer(self): + self.assertLexes("8", [8]) + self.assertLexes("12", [12]) + + def test_real(self): + self.assertLexes("1E6", [1e6]) + self.assertLexes("1E+6", [1e6]) + self.assertLexes("1E-6", [1e-6]) + self.assertLexes("1.1E6", [1.1e6]) + self.assertLexes("1.1", [1.1]) + + def test_bits(self): + self.assertLexes("(0)", [bits("")]) + self.assertLexes("(1)", [bits("1")]) + self.assertLexes("(F)", [bits("1111")]) + self.assertLexes("(f)", [bits("1111")]) + self.assertLexes("(0F)", [bits("1111")]) + self.assertLexes("(A\n5)", [bits("10100101")]) # Test literals split over two lines + self.assertLexes("(A\n\t5)", [bits("10100101")]) # With potential whitespace + self.assertLexes("(A\n 5)", [bits("10100101")]) + self.assertLexes("(A\r\n5)", [bits("10100101")]) # Support both LF & LFCR + self.assertLexes("(A\r\n\t5)", [bits("10100101")]) + self.assertLexes("(A\r\n 5)", [bits("10100101")]) + self.assertLexes("(FF)", [bits("11111111")]) + self.assertLexes("(1AA)", [bits("110101010")]) + + def test_literal(self): + self.assertLexes("(HHZZL)", [("HHZZL",)]) + self.assertLexes("(IN FOO)", [("IN FOO",)]) + + def test_error(self): + with self.assertRaises(SVFParsingError): + SVFLexer("XXX").next() + + +class SVFMockEventHandler: + def __init__(self): + self.events = [] + + def __getattr__(self, name): + if name.startswith("svf_"): + def svf_event(**kwargs): + self.events.append((name, kwargs)) + return svf_event + else: + return super().__getattr__(name) + + +class SVFParserTestCase(unittest.TestCase): + def setUp(self): + self.maxDiff = None + + def assertParses(self, source, events): + self.handler = SVFMockEventHandler() + self.parser = SVFParser(source, self.handler) + self.parser.parse_file() + self.assertEqual(self.handler.events, events) + + def assertErrors(self, source, error): + with self.assertRaisesRegex(SVFParsingError, r"^{}".format(re.escape(error))): + self.handler = SVFMockEventHandler() + self.parser = SVFParser(source, self.handler) + self.parser.parse_file() + + def test_frequency(self): + self.assertParses("FREQUENCY;", + [("svf_frequency", {"frequency": None})]) + self.assertParses("FREQUENCY 1E6 HZ;", + [("svf_frequency", {"frequency": 1e6})]) + self.assertParses("FREQUENCY 1000 HZ;", + [("svf_frequency", {"frequency": 1000})]) + + self.assertErrors("FREQUENCY 1E6;", + "expected HZ") + + def test_trst(self): + self.assertParses("TRST ON;", + [("svf_trst", {"mode": "ON"})]) + self.assertParses("TRST OFF;", + [("svf_trst", {"mode": "OFF"})]) + self.assertParses("TRST Z;", + [("svf_trst", {"mode": "Z"})]) + self.assertParses("TRST ABSENT;", + [("svf_trst", {"mode": "ABSENT"})]) + + self.assertErrors("TRST HZ;", + "expected TRST mode") + + def test_state(self): + self.assertParses("STATE IDLE;", + [("svf_state", {"state": "IDLE", "path": []})]) + self.assertParses("STATE IRUPDATE IDLE;", + [("svf_state", {"state": "IDLE", "path": ["IRUPDATE"]})]) + self.assertParses("STATE IREXIT2 IRUPDATE IDLE;", + [("svf_state", {"state": "IDLE", "path": ["IREXIT2", "IRUPDATE"]})]) + + self.assertErrors("STATE;", + "at least one state required") + self.assertErrors("STATE IRSHIFT;", + "last state must be a stable state") + self.assertErrors("STATE RESET IRSHIFT;", + "last state must be a stable state") + + def test_endir_enddr(self): + for command, event in [ + ("ENDIR", "svf_endir"), + ("ENDDR", "svf_enddr") + ]: + self.assertParses("{c} IRPAUSE;".format(c=command), + [(event, {"state": "IRPAUSE"})]) + + self.assertErrors("{c} IRSHIFT;".format(c=command), + "expected stable TAP state") + self.assertErrors("{c};".format(c=command), + "expected stable TAP state") + + def test_hir_sir_tir_hdr_sdr_tdr(self): + for command, event in [ + ("HIR", "svf_hir"), + ("SIR", "svf_sir"), + ("TIR", "svf_tir"), + ("HDR", "svf_hdr"), + ("SDR", "svf_sdr"), + ("TDR", "svf_tdr"), + ]: + self.assertParses("{c} 0;".format(c=command), [ + (event, { + "tdi": bits(""), + "smask": bits(""), + "tdo": None, + "mask": bits(""), + }), + ]) + self.assertParses("{c} 8 TDI(a);".format(c=command), [ + (event, { + "tdi": bits("00001010"), + "smask": bits("11111111"), + "tdo": None, + "mask": bits("00000000"), + }), + ]) + self.assertParses("{c} 6 TDI(0a);".format(c=command), [ + (event, { + "tdi": bits("001010"), + "smask": bits("111111"), + "tdo": None, + "mask": bits("000000"), + }), + ]) + self.assertParses("{c} 8 TDI(a); {c} 8;".format(c=command), [ + (event, { + "tdi": bits("00001010"), + "smask": bits("11111111"), + "tdo": None, + "mask": bits("00000000"), + }), + (event, { + "tdi": bits("00001010"), + "smask": bits("11111111"), + "tdo": None, + "mask": bits("00000000"), + }), + ]) + self.assertParses("{c} 8 TDI(a) SMASK(3); {c} 8; {c} 12 TDI(b);" + .format(c=command), [ + (event, { + "tdi": bits("00001010"), + "smask": bits("00000011"), + "tdo": None, + "mask": bits("00000000"), + }), + (event, { + "tdi": bits("00001010"), + "smask": bits("00000011"), + "tdo": None, + "mask": bits("00000000"), + }), + (event, { + "tdi": bits("000000001011"), + "smask": bits("111111111111"), + "tdo": None, + "mask": bits("000000000000"), + }), + ]) + self.assertParses("{c} 8 TDI(0) TDO(a) MASK(3); {c} 8; " + "{c} 8 TDO(1); {c} 12 TDI(0) TDO(b);" + .format(c=command), [ + (event, { + "tdi": bits("00000000"), + "smask": bits("11111111"), + "tdo": bits("00001010"), + "mask": bits("00000011"), + }), + (event, { + "tdi": bits("00000000"), + "smask": bits("11111111"), + "tdo": None, + "mask": bits("00000000"), + }), + (event, { + "tdi": bits("00000000"), + "smask": bits("11111111"), + "tdo": bits("00000001"), + "mask": bits("00000011"), + }), + (event, { + "tdi": bits("000000000000"), + "smask": bits("111111111111"), + "tdo": bits("000000001011"), + "mask": bits("111111111111"), + }), + ]) + + self.assertErrors("{c} 8 TDI(aaa);".format(c=command), + "scan data length 12 exceeds command length 8") + self.assertErrors("{c} 8 TDI(0) TDI(0);".format(c=command), + "parameter TDI specified twice") + self.assertErrors("{c} 8;".format(c=command), + "initial value for parameter TDI required") + self.assertErrors("{c} 8 TDI(aa); {c} 12;".format(c=command), + "parameter TDI needs to be specified again because " + "the length changed") + + def test_runtest(self): + self.assertParses("RUNTEST 20000 TCK;", [ + ("svf_runtest", { + "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", + "min_time": None, "max_time": None, "end_state": "IDLE" + }), + ]) + self.assertParses("RUNTEST 20000 TCK 1E3 SEC;", [ + ("svf_runtest", { + "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", + "min_time": 1e3, "max_time": None, "end_state": "IDLE" + }), + ]) + self.assertParses("RUNTEST 20000 TCK 1E3 SEC MAXIMUM 1E6 SEC;", [ + ("svf_runtest", { + "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", + "min_time": 1e3, "max_time": 1e6, "end_state": "IDLE" + }), + ]) + self.assertParses("RUNTEST 20000 TCK 1E3 SEC MAXIMUM 1E6 SEC ENDSTATE RESET;", [ + ("svf_runtest", { + "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", + "min_time": 1e3, "max_time": 1e6, "end_state": "RESET" + }), + ]) + self.assertParses("RUNTEST 20000 TCK 1E3 SEC MAXIMUM 1E6 SEC ENDSTATE RESET;", [ + ("svf_runtest", { + "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", + "min_time": 1e3, "max_time": 1e6, "end_state": "RESET" + }), + ]) + self.assertParses("RUNTEST 20000 TCK ENDSTATE RESET; RUNTEST 100 TCK;", [ + ("svf_runtest", { + "run_state": "IDLE", "run_count": 20000, "run_clock": "TCK", + "min_time": None, "max_time": None, "end_state": "RESET" + }), + ("svf_runtest", { + "run_state": "IDLE", "run_count": 100, "run_clock": "TCK", + "min_time": None, "max_time": None, "end_state": "RESET" + }), + ]) + self.assertParses("RUNTEST RESET 20000 TCK ENDSTATE RESET; RUNTEST IDLE 100 TCK;", [ + ("svf_runtest", { + "run_state": "RESET", "run_count": 20000, "run_clock": "TCK", + "min_time": None, "max_time": None, "end_state": "RESET" + }), + ("svf_runtest", { + "run_state": "IDLE", "run_count": 100, "run_clock": "TCK", + "min_time": None, "max_time": None, "end_state": "IDLE" + }), + ]) + + self.assertParses("RUNTEST 20000 SCK;", [ + ("svf_runtest", { + "run_state": "IDLE", "run_count": 20000, "run_clock": "SCK", + "min_time": None, "max_time": None, "end_state": "IDLE" + }), + ]) + + self.assertParses("RUNTEST 1 SEC;", [ + ("svf_runtest", { + "run_state": "IDLE", "run_count": None, "run_clock": "TCK", + "min_time": 1, "max_time": None, "end_state": "IDLE" + }), + ]) + self.assertParses("RUNTEST 1 SEC MAXIMUM 2 SEC;", [ + ("svf_runtest", { + "run_state": "IDLE", "run_count": None, "run_clock": "TCK", + "min_time": 1, "max_time": 2, "end_state": "IDLE" + }), + ]) + self.assertParses("RUNTEST 200 TCK ENDSTATE RESET; RUNTEST 1 SEC;", [ + ("svf_runtest", { + "run_state": "IDLE", "run_count": 200, "run_clock": "TCK", + "min_time": None, "max_time": None, "end_state": "RESET" + }), + ("svf_runtest", { + "run_state": "IDLE", "run_count": None, "run_clock": "TCK", + "min_time": 1, "max_time": None, "end_state": "RESET" + }), + ]) + + self.assertErrors("RUNTEST;", + "expected number") + self.assertErrors("RUNTEST 2 SEC MAXIMUM 1 SEC;", + "maximum time must be greater than minimum time") + + def test_piomap(self): + self.assertParses("PIOMAP (IN FOO OUT BAR);", + [("svf_piomap", {"mapping": "IN FOO OUT BAR"})]) + + self.assertErrors("PIOMAP;", + "expected data") + + def test_pio(self): + self.assertParses("PIO (LHZX);", + [("svf_pio", {"vector": "LHZX"})]) + + self.assertErrors("PIO;", + "expected data") + + def test_last_command(self): + handler = SVFMockEventHandler() + parser = SVFParser(" TRST OFF; SIR 8 TDI (aa); ", handler) + parser.parse_command() + self.assertEqual(parser.last_command(), " TRST OFF;") + parser.parse_command() + self.assertEqual(parser.last_command(), " SIR 8 TDI (aa);") + +# ------------------------------------------------------------------------------------------------- + +class SVFPrintingEventHandler: + def __getattr__(self, name): + if name.startswith("svf_"): + def svf_event(**kwargs): + print((name, kwargs)) + return svf_event + else: + return super().__getattr__(name) + + +if __name__ == "__main__": + import sys + with open(sys.argv[1]) as f: + SVFParser(f.read(), SVFPrintingEventHandler()).parse_file() diff --git a/software/tests/support/__init__.py b/software/tests/support/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/software/tests/support/test_bits.py b/software/tests/support/test_bits.py new file mode 100644 index 000000000..061688b55 --- /dev/null +++ b/software/tests/support/test_bits.py @@ -0,0 +1,180 @@ +import unittest + +from glasgow.support.bits import bits + + +class BitsTestCase(unittest.TestCase): + def assertBits(self, value, bit_length, bit_value): + self.assertIsInstance(value, bits) + self.assertEqual(value._len_, bit_length) + self.assertEqual(value._int_, bit_value) + + def test_from_int(self): + self.assertBits(bits.from_int(0), 0, 0b0) + self.assertBits(bits.from_int(1), 1, 0b1) + self.assertBits(bits.from_int(2), 2, 0b10) + self.assertBits(bits.from_int(2, 5), 5, 0b00010) + self.assertBits(bits.from_int(0b110, 2), 2, 0b10) + self.assertBits(bits.from_int(-1, 16), 16, 0xffff) + + def test_from_int_wrong(self): + with self.assertRaisesRegex(ValueError, + r"invalid negative input for bits\(\): '-1'"): + bits.from_int(-1) + + def test_from_str(self): + self.assertBits(bits.from_str(""), 0, 0b0) + self.assertBits(bits.from_str("0"), 1, 0b0) + self.assertBits(bits.from_str("010"), 3, 0b010) + self.assertBits(bits.from_str("0 1 011_100"), 8, 0b01011100) + self.assertBits(bits.from_str("+0 1 \t011_100"), 8, 0b01011100) + + def test_from_str_wrong(self): + with self.assertRaisesRegex(ValueError, + r"invalid negative input for bits\(\): '-1'"): + bits.from_str("-1") + with self.assertRaisesRegex(ValueError, + r"invalid literal for int\(\) with base 2: '23'"): + bits.from_str("23") + + def test_from_bytes(self): + self.assertBits(bits.from_bytes(b"\xa5", 8), 8, 0b10100101) + self.assertBits(bits.from_bytes(b"\xa5\x01", 9), 9, 0b110100101) + self.assertBits(bits.from_bytes(b"\xa5\xff", 9), 9, 0b110100101) + + def test_from_iter(self): + self.assertBits(bits.from_iter(iter([])), 0, 0b0) + self.assertBits(bits.from_iter(iter([1,1,0,1,0,0,1])), 7, 0b1001011) + + def test_new(self): + self.assertBits(bits(), 0, 0b0) + self.assertBits(bits(10), 4, 0b1010) + self.assertBits(bits(10, 2), 2, 0b10) + self.assertBits(bits("1001"), 4, 0b1001) + self.assertBits(bits(b"\xa5\x01", 9), 9, 0b110100101) + self.assertBits(bits(bytearray(b"\xa5\x01"), 9), 9, 0b110100101) + self.assertBits(bits(memoryview(b"\xa5\x01"), 9), 9, 0b110100101) + self.assertBits(bits([1,1,0,1,0,0,1]), 7, 0b1001011) + self.assertBits(bits(bits("1001"), 2), 2, 0b01) + some = bits("1001") + self.assertIs(bits(some), some) + + def test_new_wrong(self): + with self.assertRaisesRegex(TypeError, + r"invalid input for bits\(\): cannot convert from float"): + bits(1.0) + with self.assertRaisesRegex(ValueError, + r"invalid input for bits\(\): when converting from str " + r"length must not be provided"): + bits("1010", 5) + with self.assertRaisesRegex(ValueError, + r"invalid input for bits\(\): when converting from bytes " + r"length must be provided"): + bits(b"\xa5") + with self.assertRaisesRegex(ValueError, + r"invalid input for bits\(\): when converting from an iterable " + r"length must not be provided"): + bits([1,0,1,0], 5) + + def test_len(self): + self.assertEqual(len(bits(10)), 4) + + def test_bool(self): + self.assertFalse(bits("")) + self.assertTrue(bits("1")) + self.assertTrue(bits("01")) + self.assertTrue(bits("0")) + self.assertTrue(bits("00")) + + def test_int(self): + self.assertEqual(int(bits("1010")), 0b1010) + + def test_str(self): + self.assertEqual(str(bits("")), "") + self.assertEqual(str(bits("0000")), "0000") + self.assertEqual(str(bits("1010")), "1010") + self.assertEqual(str(bits("01010")), "01010") + + def test_bytes(self): + self.assertEqual(bytes(bits("")), b"") + self.assertEqual(bytes(bits("10100101")), b"\xa5") + self.assertEqual(bytes(bits("110100101")), b"\xa5\x01") + + def test_repr(self): + self.assertEqual(repr(bits("")), r"bits('')") + self.assertEqual(repr(bits("1010")), r"bits('1010')") + + def test_getitem_int(self): + some = bits("10001001011") + self.assertEqual(some[0], 1) + self.assertEqual(some[2], 0) + self.assertEqual(some[5], 0) + self.assertEqual(some[-1], 1) + self.assertEqual(some[-2], 0) + self.assertEqual(some[-5], 1) + + def test_getitem_slice(self): + some = bits("10001001011") + self.assertBits(some[:], 11, 0b10001001011) + self.assertBits(some[2:], 9, 0b100010010) + self.assertBits(some[2:9], 7, 0b0010010) + self.assertBits(some[2:-2], 7, 0b0010010) + self.assertBits(some[3:2], 0, 0b0) + + def test_getitem_wrong(self): + with self.assertRaisesRegex(TypeError, + r"bits indices must be integers or slices, not str"): + bits()["x"] + + def test_iter(self): + some = bits("10001001011") + self.assertEqual(list(some), [1,1,0,1,0,0,1,0,0,0,1]) + + def test_eq(self): + self.assertEqual(bits("1010"), 0b1010) + self.assertEqual(bits("1010"), "1010") + self.assertEqual(bits("1010"), bits("1010")) + self.assertNotEqual(bits("0010"), 0b0010) + self.assertNotEqual(bits("0010"), "010") + self.assertNotEqual(bits("1010"), bits("01010")) + self.assertNotEqual(bits("1010"), None) + + def test_add(self): + self.assertBits(bits("1010") + bits("1110"), 8, 0b11101010) + self.assertBits(bits("1010") + (0,1,1,1), 8, 0b11101010) + self.assertBits((0,1,1,1) + bits("1010"), 8, 0b10101110) + + def test_mul(self): + self.assertBits(bits("1011") * 4, 16, 0b1011101110111011) + self.assertBits(4 * bits("1011"), 16, 0b1011101110111011) + + def test_and(self): + self.assertBits(bits("1010") & bits("1100"), 4, 0b1000) + self.assertBits(bits("1010") & "1100", 4, 0b1000) + self.assertBits((0,1,0,1) & bits("1100"), 4, 0b1000) + + def test_or(self): + self.assertBits(bits("1010") | bits("1100"), 4, 0b1110) + self.assertBits(bits("1010") | "1100", 4, 0b1110) + self.assertBits((0,1,0,1) | bits("1100"), 4, 0b1110) + + def test_xor(self): + self.assertBits(bits("1010") ^ bits("1100"), 4, 0b0110) + self.assertBits(bits("1010") ^ "1100", 4, 0b0110) + self.assertBits((0,1,0,1) ^ bits("1100"), 4, 0b0110) + + def test_reversed(self): + self.assertBits(bits("1010").reversed(), 4, 0b0101) + + def test_find(self): + self.assertEqual(bits("1011").find(bits("11")), 0) + self.assertEqual(bits("1011").find(bits("10")), 2) + self.assertEqual(bits("1011").find(bits("01")), 1) + self.assertEqual(bits("1011").find(bits("00")), -1) + + self.assertEqual(bits("101100101").find(bits("10"), 0), 1) + self.assertEqual(bits("101100101").find(bits("10"), 2), 4) + self.assertEqual(bits("101100101").find(bits("10"), 5), 7) + self.assertEqual(bits("101100101").find(bits("10"), 8), -1) + + self.assertEqual(bits("1011").find(bits((1,0))), 1) diff --git a/software/tests/support/test_bitstruct.py b/software/tests/support/test_bitstruct.py new file mode 100644 index 000000000..11b24f740 --- /dev/null +++ b/software/tests/support/test_bitstruct.py @@ -0,0 +1,122 @@ +import unittest + +from glasgow.support.bits import bits +from glasgow.support.bitstruct import bitstruct + + +class BitstructTestCase(unittest.TestCase): + def test_definition(self): + bs = bitstruct("bs", 10, [("a", 3), ("b", 5), (None, 2)]) + self.assertEqual(bs.__name__, "bs") + self.assertEqual(bs.__module__, __name__) + x = bs(1, 2) + self.assertEqual(x.a, 1) + self.assertEqual(x.b, 2) + self.assertEqual(bs.bit_length(), 10) + self.assertEqual(x.bit_length(), 10) + + def test_misuse(self): + with self.assertRaises(TypeError): + bitstruct("bs", 10, [("a", 3), ("b", 5)]) + + bs = bitstruct("bs", 10, [("a", 3), ("b", 5), (None, 2)]) + + with self.assertRaises(TypeError): + bs(1, 2, b=3) + + with self.assertRaises(TypeError): + bs(c=3) + + x = bs() + with self.assertRaises(ValueError): + x.a = -1 + with self.assertRaises(ValueError): + x.a = 8 + with self.assertRaises(ValueError): + x.a = bits("1") + with self.assertRaises(ValueError): + x.a = bits("1111") + + with self.assertRaises(ValueError): + bs.from_bytes(bytes(3)) + with self.assertRaises(ValueError): + bs.from_bytes(bytes(1)) + with self.assertRaises(ValueError): + bs.from_bits(bits(0, 9)) + with self.assertRaises(ValueError): + bs.from_bits(bits(0, 11)) + with self.assertRaises(ValueError): + bs.from_int(-1) + with self.assertRaises(ValueError): + bs.from_int(1<<10) + + def test_kwargs(self): + bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) + x = bs(a=1, b=2) + self.assertEqual(x.a, 1) + self.assertEqual(x.b, 2) + + def test_large(self): + bs = bitstruct("bs", 72, [(None, 8), ("a", 64)]) + val = (3 << 62) + 1 + x = bs(val) + self.assertEqual(x.to_int(), val << 8) + + def test_huge(self): + bs = bitstruct("bs", 2080, [("e", 32), ("m", 2048)]) + x = bs(65537, (30<<2048) // 31) + self.assertEqual(x.e, 65537) + self.assertEqual(x.m, (30<<2048) // 31) + + def test_reserved(self): + bs = bitstruct("bs", 64, [(None, 1), ("a", 1), (None, 62)]) + x = bs(1) + self.assertEqual(repr(x), "<%s.bs a=1>" % __name__) + + def test_bytes(self): + bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) + x = bs(1, 2) + self.assertIsInstance(x.to_bytes(), bytes) + self.assertEqual(x.to_bytes(), b"\x11") + self.assertEqual(bs.from_bytes(x.to_bytes()), x) + + def test_bytearray(self): + bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) + x = bs(1, 2) + self.assertIsInstance(x.to_bytearray(), bytearray) + self.assertEqual(x.to_bytearray(), bytearray(b"\x11")) + self.assertEqual(bs.from_bytearray(x.to_bytearray()), x) + + def test_int(self): + bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) + x = bs(1, 2) + self.assertIsInstance(x.to_int(), int) + self.assertEqual(x.to_int(), 17) + self.assertEqual(bs.from_int(x.to_int()), x) + + def test_bits(self): + bs = bitstruct("bs", 10, [("a", 3), ("b", 7)]) + x = bs(1, 2) + self.assertIsInstance(x.to_bits(), bits) + self.assertEqual(x.to_bits(), bits("0000010001")) + self.assertEqual(bs.from_bits(x.to_bits()), x) + + def test_repr(self): + bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) + x = bs(1, 2) + self.assertEqual(repr(x), "<%s.bs a=001 b=00010>" % __name__) + + def test_copy(self): + bs = bitstruct("bs", 8, [("a", 3), ("b", 5)]) + x1 = bs(1, 2) + x2 = x1.copy() + self.assertFalse(x1 is x2) + self.assertEqual(x1, x2) + + def test_slots(self): + bs = bitstruct("bs", 8, [("a", 8)]) + x = bs() + with self.assertRaises(AttributeError): + x.b + with self.assertRaises(AttributeError): + x.b = 1 diff --git a/software/tests/support/test_chunked_fifo.py b/software/tests/support/test_chunked_fifo.py new file mode 100644 index 000000000..755fc6da4 --- /dev/null +++ b/software/tests/support/test_chunked_fifo.py @@ -0,0 +1,76 @@ +import unittest + +from glasgow.support.bits import bits +from glasgow.support.chunked_fifo import ChunkedFIFO + + +class ChunkedFIFOTestCase(unittest.TestCase): + def setUp(self): + self.fifo = ChunkedFIFO() + + def test_zero_write(self): + self.fifo.write(b"") + with self.assertRaises(IndexError): + self.fifo.read() + + def test_zero_read(self): + self.assertEqual(self.fifo.read(0), b"") + self.fifo.write(b"A") + self.assertEqual(self.fifo.read(0), b"") + self.assertEqual(self.fifo.read(), b"A") + + def test_fast(self): + self.fifo.write(b"AB") + self.fifo.write(b"CD") + self.assertEqual(self.fifo.read(), b"AB") + self.assertEqual(self.fifo.read(), b"CD") + + def test_chunked(self): + self.fifo.write(b"ABCD") + self.fifo.write(b"EF") + self.assertEqual(self.fifo.read(1), b"A") + self.assertEqual(self.fifo.read(1), b"B") + self.assertEqual(self.fifo.read(2), b"CD") + self.assertEqual(self.fifo.read(), b"EF") + + def test_chunked_chunked(self): + self.fifo.write(b"ABCD") + self.fifo.write(b"EF") + self.assertEqual(self.fifo.read(1), b"A") + self.assertEqual(self.fifo.read(1), b"B") + self.assertEqual(self.fifo.read(2), b"CD") + self.assertEqual(self.fifo.read(1), b"E") + self.assertEqual(self.fifo.read(1), b"F") + + def test_chunked_fast(self): + self.fifo.write(b"ABCD") + self.fifo.write(b"EF") + self.assertEqual(self.fifo.read(1), b"A") + self.assertEqual(self.fifo.read(1), b"B") + self.assertEqual(self.fifo.read(), b"CD") + self.assertEqual(self.fifo.read(), b"EF") + + def test_bool(self): + self.assertFalse(self.fifo) + self.fifo.write(b"ABC") + self.assertTrue(self.fifo) + self.fifo.read(1) + self.assertTrue(self.fifo) + self.fifo.read(2) + self.assertFalse(self.fifo) + + def test_len(self): + self.assertEqual(len(self.fifo), 0) + self.fifo.write(b"ABCD") + self.assertEqual(len(self.fifo), 4) + self.fifo.write(b"EF") + self.assertEqual(len(self.fifo), 6) + self.fifo.read(1) + self.assertEqual(len(self.fifo), 5) + self.fifo.read(3) + self.assertEqual(len(self.fifo), 2) + + def test_write_bits(self): + self.fifo.write(bits("1010")) + self.assertEqual(len(self.fifo), 1) + self.assertEqual(self.fifo.read(1), b"\x0a") diff --git a/software/tests/support/test_endpoint.py b/software/tests/support/test_endpoint.py new file mode 100644 index 000000000..94dc52f49 --- /dev/null +++ b/software/tests/support/test_endpoint.py @@ -0,0 +1,108 @@ +import asyncio +import logging +import unittest +import tempfile + +from glasgow.support.endpoint import endpoint, ServerEndpoint + + +class EndpointArgumentTestCase(unittest.TestCase): + def test_unix(self): + proto, path = endpoint("unix:/foo/bar") + self.assertEqual(proto, "unix") + self.assertEqual(path, "/foo/bar") + + def test_tcp_localhost(self): + proto, host, port = endpoint("tcp::1234") + self.assertEqual(proto, "tcp") + self.assertEqual(host, "localhost") + self.assertEqual(port, 1234) + + def test_tcp_all(self): + proto, host, port = endpoint("tcp:*:1234") + self.assertEqual(proto, "tcp") + self.assertEqual(host, None) + self.assertEqual(port, 1234) + + def test_tcp_ipv4(self): + proto, host, port = endpoint("tcp:0.0.0.0:1234") + self.assertEqual(proto, "tcp") + self.assertEqual(host, "0.0.0.0") + self.assertEqual(port, 1234) + + def test_tcp_ipv6(self): + proto, host, port = endpoint("tcp:[2001:DB8::1]:1234") + self.assertEqual(proto, "tcp") + self.assertEqual(host, "2001:DB8::1") + self.assertEqual(port, 1234) + + def test_tcp_hostname(self): + proto, host, port = endpoint("tcp:eXample.org:1234") + self.assertEqual(proto, "tcp") + self.assertEqual(host, "eXample.org") + self.assertEqual(port, 1234) + + +class ServerEndpointTestCase(unittest.TestCase): + async def do_test_lifecycle(self): + sock = ("unix", "{}/test_lifecycle_sock".format(tempfile.gettempdir())) + endp = await ServerEndpoint("test_lifecycle", logging.getLogger(__name__), sock) + + conn_rd, conn_wr = await asyncio.open_unix_connection(*sock[1:]) + conn_wr.write(b"ABC") + await conn_wr.drain() + self.assertEqual(await endp.recv(1), b"A") + self.assertEqual(await endp.recv(2), b"BC") + await endp.send(b"XYZ") + self.assertEqual(await conn_rd.readexactly(2), b"XY") + self.assertEqual(await conn_rd.readexactly(1), b"Z") + await endp.close() + self.assertEqual(await conn_rd.read(1), b"") + with self.assertRaises(asyncio.CancelledError): + await endp.recv(1) + conn_wr.close() + + conn_rd, conn_wr = await asyncio.open_unix_connection(*sock[1:]) + conn_wr.write(b"ABC") + await conn_wr.drain() + self.assertEqual(await endp.recv(3), b"ABC") + conn_wr.close() + with self.assertRaises(asyncio.CancelledError): + await endp.recv(1) + + def test_lifecycle(self): + asyncio.get_event_loop().run_until_complete( + self.do_test_lifecycle()) + + async def do_test_until(self): + sock = ("unix", "{}/test_until_sock".format(tempfile.gettempdir())) + endp = await ServerEndpoint("test_until", logging.getLogger(__name__), sock) + + conn_rd, conn_wr = await asyncio.open_unix_connection(*sock[1:]) + conn_wr.write(b"ABC;DEF") + await conn_wr.drain() + self.assertEqual(await endp.recv_until(b";"), b"ABC") + conn_wr.write(b";") + await conn_wr.drain() + self.assertEqual(await endp.recv_until(b";"), b"DEF") + + def test_until(self): + asyncio.get_event_loop().run_until_complete( + self.do_test_until()) + + async def do_test_tcp(self): + sock = ("tcp", "localhost", 9999) + endp = await ServerEndpoint("test_tcp", logging.getLogger(__name__), sock) + + conn_rd, conn_wr = await asyncio.open_connection(*sock[1:]) + conn_wr.write(b"ABC") + await conn_wr.drain() + self.assertEqual(await endp.recv(3), b"ABC") + await endp.send(b"XYZ") + self.assertEqual(await conn_rd.readexactly(3), b"XYZ") + conn_wr.close() + self.assertEqual(await endp.recv(1), None) + + def test_tcp(self): + asyncio.get_event_loop().run_until_complete( + self.do_test_lifecycle()) diff --git a/software/tests/support/test_lazy.py b/software/tests/support/test_lazy.py new file mode 100644 index 000000000..6fc53f836 --- /dev/null +++ b/software/tests/support/test_lazy.py @@ -0,0 +1,41 @@ +import unittest + +from glasgow.support.lazy import lazy + + +class _test(object): + pass + + +class LazyTestCase(unittest.TestCase): + def test_get(self): + x = _test() + x.f = 0 + y = lazy(lambda: x) + x.f = 1 + self.assertEqual(y.f, 1) + + def test_set(self): + x = _test() + x.f = 0 + y = lazy(lambda: x) + y.f = 1 + self.assertEqual(x.f, 1) + + def test_del(self): + x = _test() + x.f = 0 + y = lazy(lambda: x) + del y.f + with self.assertRaises(AttributeError): + x.f + + def test_str(self): + x = lazy(lambda: "foo") + self.assertEqual(str(x), "foo") + + def test_repr(self): + x = lazy(lambda: "foo") + self.assertTrue(repr(x).startswith(" Date: Fri, 20 Oct 2023 14:34:34 +0200 Subject: [PATCH 3/4] software: get rid of test.py wrapper --- software/pyproject.toml | 5 +---- software/test.py | 8 -------- 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 software/test.py diff --git a/software/pyproject.toml b/software/pyproject.toml index 16bc08876..fc6dac3f0 100644 --- a/software/pyproject.toml +++ b/software/pyproject.toml @@ -116,8 +116,5 @@ build-backend = "pdm.backend" # Development workflow configuration -[tool.pdm.dev-dependencies] -test = ["setuptools"] - [tool.pdm.scripts] -test = {cmd = "test.py -v"} +test = {cmd = "python -m unittest"} diff --git a/software/test.py b/software/test.py deleted file mode 100644 index 0470583e9..000000000 --- a/software/test.py +++ /dev/null @@ -1,8 +0,0 @@ -# Top-level interface to run the Glasgow tests. - -import unittest -from setuptools.command.test import ScanningLoader - - -if __name__ == "__main__": - unittest.main(module="glasgow", testLoader=ScanningLoader()) From e30d5bbed7931393099dc90050c8ae1c1685c3ea Mon Sep 17 00:00:00 2001 From: Wanda Date: Sat, 21 Oct 2023 18:58:54 +0200 Subject: [PATCH 4/4] software: reimplement `glasgow test` discovery. --- software/glasgow/applet/__init__.py | 5 ++++- software/glasgow/applet/audio/dac/__init__.py | 5 +++++ software/glasgow/applet/audio/yamaha_opx/__init__.py | 5 +++++ software/glasgow/applet/display/hd44780/__init__.py | 5 +++++ software/glasgow/applet/display/pdi/__init__.py | 5 +++++ software/glasgow/applet/interface/analyzer/__init__.py | 5 +++++ software/glasgow/applet/interface/i2c_initiator/__init__.py | 5 +++++ software/glasgow/applet/interface/i2c_target/__init__.py | 5 +++++ software/glasgow/applet/interface/jtag_openocd/__init__.py | 5 +++++ software/glasgow/applet/interface/jtag_pinout/__init__.py | 5 +++++ software/glasgow/applet/interface/jtag_probe/__init__.py | 5 +++++ software/glasgow/applet/interface/ps2_host/__init__.py | 5 +++++ software/glasgow/applet/interface/sbw_probe/__init__.py | 5 +++++ .../glasgow/applet/interface/spi_controller/__init__.py | 5 +++++ software/glasgow/applet/interface/uart/__init__.py | 5 +++++ software/glasgow/applet/internal/benchmark/__init__.py | 5 +++++ software/glasgow/applet/internal/selftest/__init__.py | 5 +++++ software/glasgow/applet/memory/_25x/__init__.py | 5 +++++ software/glasgow/applet/memory/floppy/__init__.py | 5 +++++ software/glasgow/applet/memory/onfi/__init__.py | 5 +++++ software/glasgow/applet/memory/prom/__init__.py | 5 +++++ software/glasgow/applet/program/avr/spi/__init__.py | 5 +++++ software/glasgow/applet/program/ice40_flash/__init__.py | 5 +++++ software/glasgow/applet/program/ice40_sram/__init__.py | 5 +++++ software/glasgow/applet/program/m16c/__init__.py | 5 +++++ software/glasgow/applet/program/nrf24lx1/__init__.py | 5 +++++ software/glasgow/applet/radio/nrf24l01/__init__.py | 5 +++++ software/glasgow/applet/sensor/hx711/__init__.py | 5 +++++ software/glasgow/applet/sensor/pmsx003/__init__.py | 5 +++++ software/glasgow/applet/sensor/pmsx003/test.py | 2 +- software/glasgow/applet/video/hub75_output/__init__.py | 5 +++++ software/glasgow/applet/video/rgb_input/__init__.py | 5 +++++ software/glasgow/applet/video/vga_output/__init__.py | 5 +++++ software/glasgow/applet/video/ws2812_output/__init__.py | 5 +++++ software/glasgow/cli.py | 6 +++--- 35 files changed, 168 insertions(+), 5 deletions(-) diff --git a/software/glasgow/applet/__init__.py b/software/glasgow/applet/__init__.py index 7ff155989..31022eb3a 100644 --- a/software/glasgow/applet/__init__.py +++ b/software/glasgow/applet/__init__.py @@ -77,6 +77,10 @@ async def repl(self, device, args, iface): await AsyncInteractiveConsole(locals={"device":device, "iface":iface, "args":args}, run_callback=device.demultiplexer.flush).interact() + @classmethod + def tests(cls): + return None + class GlasgowAppletTool: def __init_subclass__(cls, applet, **kwargs): @@ -205,7 +209,6 @@ class GlasgowAppletTestCase(unittest.TestCase): def __init_subclass__(cls, applet, **kwargs): super().__init_subclass__(**kwargs) - applet.test_cls = cls cls.applet_cls = applet def setUp(self): diff --git a/software/glasgow/applet/audio/dac/__init__.py b/software/glasgow/applet/audio/dac/__init__.py index 8e4abdb26..4124c8472 100644 --- a/software/glasgow/applet/audio/dac/__init__.py +++ b/software/glasgow/applet/audio/dac/__init__.py @@ -233,3 +233,8 @@ async def interact(self, device, args, pcm_iface): data = await reader.read(512) await pcm_iface.write(data) await pcm_iface.flush(wait=False) + + @classmethod + def tests(cls): + from . import test + return test.AudioDACAppletTestCase diff --git a/software/glasgow/applet/audio/yamaha_opx/__init__.py b/software/glasgow/applet/audio/yamaha_opx/__init__.py index ade224030..a9b32469e 100644 --- a/software/glasgow/applet/audio/yamaha_opx/__init__.py +++ b/software/glasgow/applet/audio/yamaha_opx/__init__.py @@ -1248,3 +1248,8 @@ async def set_voltage(voltage): for fut in done: await fut args.pcm_file.write(record_fut.result()) + + @classmethod + def tests(cls): + from . import test + return test.AudioYamahaOPxAppletTestCase diff --git a/software/glasgow/applet/display/hd44780/__init__.py b/software/glasgow/applet/display/hd44780/__init__.py index 839db97a6..045f1f4fe 100644 --- a/software/glasgow/applet/display/hd44780/__init__.py +++ b/software/glasgow/applet/display/hd44780/__init__.py @@ -278,3 +278,8 @@ async def data(bytes): await cmd(CMD_DDRAM_ADDRESS|0x40) await data(datetime.now().strftime("%y-%m-%d").encode("ascii")) await iface.flush() + + @classmethod + def tests(cls): + from . import test + return test.DisplayHD44780AppletTestCase diff --git a/software/glasgow/applet/display/pdi/__init__.py b/software/glasgow/applet/display/pdi/__init__.py index a324704c7..0bcbc49b1 100644 --- a/software/glasgow/applet/display/pdi/__init__.py +++ b/software/glasgow/applet/display/pdi/__init__.py @@ -476,3 +476,8 @@ async def interact(self, device, args, pdi_iface): await pdi_iface.display_frame(mode="white", time_ms=stage_ms, image=image) await pdi_iface.display_frame(mode="white", time_ms=stage_ms, image=image) await pdi_iface.power_off() + + @classmethod + def tests(cls): + from . import test + return test.DisplayPDIAppletTestCase diff --git a/software/glasgow/applet/interface/analyzer/__init__.py b/software/glasgow/applet/interface/analyzer/__init__.py index a2e7f7da3..67a5978e5 100644 --- a/software/glasgow/applet/interface/analyzer/__init__.py +++ b/software/glasgow/applet/interface/analyzer/__init__.py @@ -124,3 +124,8 @@ async def interact(self, device, args, iface): finally: vcd_writer.close(timestamp) + + @classmethod + def tests(cls): + from . import test + return test.AnalyzerAppletTestCase diff --git a/software/glasgow/applet/interface/i2c_initiator/__init__.py b/software/glasgow/applet/interface/i2c_initiator/__init__.py index e4b77dbd4..ac3bd5d0c 100644 --- a/software/glasgow/applet/interface/i2c_initiator/__init__.py +++ b/software/glasgow/applet/interface/i2c_initiator/__init__.py @@ -347,3 +347,8 @@ async def interact(self, device, args, i2c_iface): manufacturer, part_ident, revision = device_id self.logger.info("device %s ID: manufacturer %s, part %s, revision %s", bin(addr), bin(manufacturer), bin(part_ident), bin(revision)) + + @classmethod + def tests(cls): + from . import test + return test.I2CInitiatorAppletTestCase diff --git a/software/glasgow/applet/interface/i2c_target/__init__.py b/software/glasgow/applet/interface/i2c_target/__init__.py index fa399a24f..0f8bb3370 100644 --- a/software/glasgow/applet/interface/i2c_target/__init__.py +++ b/software/glasgow/applet/interface/i2c_target/__init__.py @@ -248,3 +248,8 @@ async def run(self, device, args): async def interact(self, device, args, iface): while True: await iface.read_event() + + @classmethod + def tests(cls): + from . import test + return test.I2CTargetAppletTestCase diff --git a/software/glasgow/applet/interface/jtag_openocd/__init__.py b/software/glasgow/applet/interface/jtag_openocd/__init__.py index b3b61bd97..aee5a08f7 100644 --- a/software/glasgow/applet/interface/jtag_openocd/__init__.py +++ b/software/glasgow/applet/interface/jtag_openocd/__init__.py @@ -138,3 +138,8 @@ async def forward_in(): forward_in_fut = asyncio.ensure_future(forward_in()) await asyncio.wait([forward_out_fut, forward_in_fut], return_when=asyncio.FIRST_EXCEPTION) + + @classmethod + def tests(cls): + from . import test + return test.JTAGOpenOCDAppletTestCase diff --git a/software/glasgow/applet/interface/jtag_pinout/__init__.py b/software/glasgow/applet/interface/jtag_pinout/__init__.py index a9981e1a0..0cc35b9f2 100644 --- a/software/glasgow/applet/interface/jtag_pinout/__init__.py +++ b/software/glasgow/applet/interface/jtag_pinout/__init__.py @@ -447,3 +447,8 @@ def bits_to_str(pins): else: self.logger.warning("more than one JTAG interface detected; this is likely a false " "positive") + + @classmethod + def tests(cls): + from . import test + return test.JTAGPinoutAppletTestCase diff --git a/software/glasgow/applet/interface/jtag_probe/__init__.py b/software/glasgow/applet/interface/jtag_probe/__init__.py index c0d4483df..d361507c5 100644 --- a/software/glasgow/applet/interface/jtag_probe/__init__.py +++ b/software/glasgow/applet/interface/jtag_probe/__init__.py @@ -1099,3 +1099,8 @@ async def repl(self, device, args, jtag_iface): locals={"iface":iface}, run_callback=jtag_iface.flush ).interact() + + @classmethod + def tests(cls): + from . import test + return test.JTAGProbeAppletTestCase diff --git a/software/glasgow/applet/interface/ps2_host/__init__.py b/software/glasgow/applet/interface/ps2_host/__init__.py index 7b648bbfb..fc2e04e41 100644 --- a/software/glasgow/applet/interface/ps2_host/__init__.py +++ b/software/glasgow/applet/interface/ps2_host/__init__.py @@ -439,3 +439,8 @@ async def interact(self, device, args, iface): async def print_byte(byte): print("{:02x}".format(byte), end=" ", flush=True) await iface.stream(print_byte) + + @classmethod + def tests(cls): + from . import test + return test.PS2HostAppletTestCase diff --git a/software/glasgow/applet/interface/sbw_probe/__init__.py b/software/glasgow/applet/interface/sbw_probe/__init__.py index 8f1ccee1a..488f719b5 100644 --- a/software/glasgow/applet/interface/sbw_probe/__init__.py +++ b/software/glasgow/applet/interface/sbw_probe/__init__.py @@ -232,3 +232,8 @@ async def interact(self, device, args, sbw_iface): self.logger.error("no target detected; connection problem?") else: self.logger.info("found MSP430 core with JTAG ID %#04x", jtag_id) + + @classmethod + def tests(cls): + from . import test + return test.SpyBiWireProbeAppletTestCase diff --git a/software/glasgow/applet/interface/spi_controller/__init__.py b/software/glasgow/applet/interface/spi_controller/__init__.py index 090873596..3f475ce2e 100644 --- a/software/glasgow/applet/interface/spi_controller/__init__.py +++ b/software/glasgow/applet/interface/spi_controller/__init__.py @@ -370,3 +370,8 @@ def hex(arg): return bytes.fromhex(arg) async def interact(self, device, args, spi_iface): data = await spi_iface.transfer(args.data) print(data.hex()) + + @classmethod + def tests(cls): + from . import test + return test.SPIControllerAppletTestCase diff --git a/software/glasgow/applet/interface/uart/__init__.py b/software/glasgow/applet/interface/uart/__init__.py index ff4b65a53..2f44b734c 100644 --- a/software/glasgow/applet/interface/uart/__init__.py +++ b/software/glasgow/applet/interface/uart/__init__.py @@ -386,3 +386,8 @@ async def interact(self, device, args, uart): await self._interact_pty(uart) if args.operation == "socket": await self._interact_socket(uart, args.endpoint) + + @classmethod + def tests(cls): + from . import test + return test.UARTAppletTestCase diff --git a/software/glasgow/applet/internal/benchmark/__init__.py b/software/glasgow/applet/internal/benchmark/__init__.py index de1890004..1f65e7d3e 100644 --- a/software/glasgow/applet/internal/benchmark/__init__.py +++ b/software/glasgow/applet/internal/benchmark/__init__.py @@ -237,3 +237,8 @@ async def counter(): mode, (length / (end - begin)) / (1 << 20), (length / (end - begin)) / (1 << 17)) + + @classmethod + def tests(cls): + from . import test + return test.BenchmarkAppletTestCase diff --git a/software/glasgow/applet/internal/selftest/__init__.py b/software/glasgow/applet/internal/selftest/__init__.py index 75a41b9b6..ac9c2934a 100644 --- a/software/glasgow/applet/internal/selftest/__init__.py +++ b/software/glasgow/applet/internal/selftest/__init__.py @@ -293,3 +293,8 @@ def decode_pins(bits): for (mode, message) in report: self.logger.error("%s: %s", mode, message) raise GlasgowAppletError("self-test: FAIL") + + @classmethod + def tests(cls): + from . import test + return test.SelfTestAppletTestCase diff --git a/software/glasgow/applet/memory/_25x/__init__.py b/software/glasgow/applet/memory/_25x/__init__.py index e58bd0e9c..959ee0e5c 100644 --- a/software/glasgow/applet/memory/_25x/__init__.py +++ b/software/glasgow/applet/memory/_25x/__init__.py @@ -516,3 +516,8 @@ async def interact(self, device, args, m25x_iface): status = (status & ~MSK_PROT) | ((args.bits << 2) & MSK_PROT) await m25x_iface.write_enable() await m25x_iface.write_status(status) + + @classmethod + def tests(cls): + from . import test + return test.Memory25xAppletTestCase diff --git a/software/glasgow/applet/memory/floppy/__init__.py b/software/glasgow/applet/memory/floppy/__init__.py index 84541d7e6..d8533f4f8 100644 --- a/software/glasgow/applet/memory/floppy/__init__.py +++ b/software/glasgow/applet/memory/floppy/__init__.py @@ -710,6 +710,11 @@ async def interact(self, device, args, floppy_iface): finally: await floppy_iface.stop() + @classmethod + def tests(cls): + from . import test + return test.MemoryFloppyAppletTestCase + # ------------------------------------------------------------------------------------------------- class MemoryFloppyAppletTool(GlasgowAppletTool, applet=MemoryFloppyApplet): diff --git a/software/glasgow/applet/memory/onfi/__init__.py b/software/glasgow/applet/memory/onfi/__init__.py index 90c7a06f9..0c07d276c 100644 --- a/software/glasgow/applet/memory/onfi/__init__.py +++ b/software/glasgow/applet/memory/onfi/__init__.py @@ -751,3 +751,8 @@ async def interact(self, device, args, onfi_iface): row += block_size count -= block_size + + @classmethod + def tests(cls): + from . import test + return test.MemoryONFIAppletTestCase diff --git a/software/glasgow/applet/memory/prom/__init__.py b/software/glasgow/applet/memory/prom/__init__.py index caebd8788..db09df0b7 100644 --- a/software/glasgow/applet/memory/prom/__init__.py +++ b/software/glasgow/applet/memory/prom/__init__.py @@ -825,6 +825,11 @@ async def interact(self, device, args, prom_iface): ] }, args.file) + @classmethod + def tests(cls): + from . import test + return test.MemoryPROMAppletTestCase + # ------------------------------------------------------------------------------------------------- class MemoryPROMAppletTool(GlasgowAppletTool, applet=MemoryPROMApplet): diff --git a/software/glasgow/applet/program/avr/spi/__init__.py b/software/glasgow/applet/program/avr/spi/__init__.py index 68a470bdc..e36f61b12 100644 --- a/software/glasgow/applet/program/avr/spi/__init__.py +++ b/software/glasgow/applet/program/avr/spi/__init__.py @@ -256,3 +256,8 @@ async def run(self, device, args): spi_iface = await self.run_lower(ProgramAVRSPIApplet, device, args) avr_iface = ProgramAVRSPIInterface(spi_iface, self.logger, self.__addr_dut_reset) return avr_iface + + @classmethod + def tests(cls): + from . import test + return test.ProgramAVRSPIAppletTestCase diff --git a/software/glasgow/applet/program/ice40_flash/__init__.py b/software/glasgow/applet/program/ice40_flash/__init__.py index 405a7988d..eba4e8169 100644 --- a/software/glasgow/applet/program/ice40_flash/__init__.py +++ b/software/glasgow/applet/program/ice40_flash/__init__.py @@ -109,3 +109,8 @@ async def interact(self, device, args, ice40_iface): self.logger.info("FPGA configured from flash") else: self.logger.warning("FPGA failed to configure after releasing reset") + + @classmethod + def tests(cls): + from . import test + return test.ProgramICE40FlashAppletTestCase diff --git a/software/glasgow/applet/program/ice40_sram/__init__.py b/software/glasgow/applet/program/ice40_sram/__init__.py index fc33d49a6..974198100 100644 --- a/software/glasgow/applet/program/ice40_sram/__init__.py +++ b/software/glasgow/applet/program/ice40_sram/__init__.py @@ -125,3 +125,8 @@ async def interact(self, device, args, ice40_iface): self.logger.info("FPGA successfully configured") else: self.logger.warning("FPGA failed to configure after releasing reset") + + @classmethod + def tests(cls): + from . import test + return test.ProgramICE40SRAMAppletTestCase diff --git a/software/glasgow/applet/program/m16c/__init__.py b/software/glasgow/applet/program/m16c/__init__.py index f9904e1b7..1e0d8253b 100644 --- a/software/glasgow/applet/program/m16c/__init__.py +++ b/software/glasgow/applet/program/m16c/__init__.py @@ -482,3 +482,8 @@ async def interact(self, device, args, iface): finally: await iface.reset_application() + + @classmethod + def tests(cls): + from . import test + return test.ProgramM16CAppletTestCase diff --git a/software/glasgow/applet/program/nrf24lx1/__init__.py b/software/glasgow/applet/program/nrf24lx1/__init__.py index baacf0eff..7d27af1cb 100644 --- a/software/glasgow/applet/program/nrf24lx1/__init__.py +++ b/software/glasgow/applet/program/nrf24lx1/__init__.py @@ -420,3 +420,8 @@ async def check_read_protected(): finally: await nrf24lx1_iface.reset_application() + + @classmethod + def tests(cls): + from . import test + return test.ProgramNRF24Lx1AppletTestCase diff --git a/software/glasgow/applet/radio/nrf24l01/__init__.py b/software/glasgow/applet/radio/nrf24l01/__init__.py index 9c82d193e..302e8233b 100644 --- a/software/glasgow/applet/radio/nrf24l01/__init__.py +++ b/software/glasgow/applet/radio/nrf24l01/__init__.py @@ -576,3 +576,8 @@ async def interact(self, device, args, nrf24l01_iface): break finally: await nrf24l01_iface.disable() + + @classmethod + def tests(cls): + from . import test + return test.RadioNRF24L01AppletTestCase diff --git a/software/glasgow/applet/sensor/hx711/__init__.py b/software/glasgow/applet/sensor/hx711/__init__.py index 2062e532d..f6dc84855 100644 --- a/software/glasgow/applet/sensor/hx711/__init__.py +++ b/software/glasgow/applet/sensor/hx711/__init__.py @@ -211,3 +211,8 @@ async def interact(self, device, args, hx711): while True: sample = await hx711.sample() await data_logger.report_data(fields={"n": sample}) + + @classmethod + def tests(cls): + from . import test + return test.SensorHX711AppletTestCase diff --git a/software/glasgow/applet/sensor/pmsx003/__init__.py b/software/glasgow/applet/sensor/pmsx003/__init__.py index 9653a2aa4..df94f2614 100644 --- a/software/glasgow/applet/sensor/pmsx003/__init__.py +++ b/software/glasgow/applet/sensor/pmsx003/__init__.py @@ -161,3 +161,8 @@ async def interact(self, device, args, pmsx003): await data_logger.report_data(fields) except PMSx003Error as error: await data_logger.report_error(str(error), exception=error) + + @classmethod + def tests(cls): + from . import test + return test.SensorPMSx003AppletTestCase diff --git a/software/glasgow/applet/sensor/pmsx003/test.py b/software/glasgow/applet/sensor/pmsx003/test.py index 487c69eb7..6189f6abd 100644 --- a/software/glasgow/applet/sensor/pmsx003/test.py +++ b/software/glasgow/applet/sensor/pmsx003/test.py @@ -2,7 +2,7 @@ from . import SensorPMSx003Applet -class PMSx003AppletTestCase(GlasgowAppletTestCase, applet=SensorPMSx003Applet): +class SensorPMSx003AppletTestCase(GlasgowAppletTestCase, applet=SensorPMSx003Applet): @synthesis_test def test_build(self): self.assertBuilds() diff --git a/software/glasgow/applet/video/hub75_output/__init__.py b/software/glasgow/applet/video/hub75_output/__init__.py index ac07362f1..8a34e1870 100644 --- a/software/glasgow/applet/video/hub75_output/__init__.py +++ b/software/glasgow/applet/video/hub75_output/__init__.py @@ -162,3 +162,8 @@ def build(self, target, args): async def run(self, device, args): return await device.demultiplexer.claim_interface(self, self.mux_interface, args) + + @classmethod + def tests(cls): + from . import test + return test.VideoHub75OutputAppletTestCase diff --git a/software/glasgow/applet/video/rgb_input/__init__.py b/software/glasgow/applet/video/rgb_input/__init__.py index 9d3664599..5cc6c6e7a 100644 --- a/software/glasgow/applet/video/rgb_input/__init__.py +++ b/software/glasgow/applet/video/rgb_input/__init__.py @@ -174,3 +174,8 @@ async def run(self, device, args): row = ((sync & 0x01) << 7) | (await iface.read(1))[0] print("frame {} row {}".format(frame, row)) + + @classmethod + def tests(cls): + from . import test + return test.VideoRGBInputAppletTestCase diff --git a/software/glasgow/applet/video/vga_output/__init__.py b/software/glasgow/applet/video/vga_output/__init__.py index 0038c14be..c0f437a83 100644 --- a/software/glasgow/applet/video/vga_output/__init__.py +++ b/software/glasgow/applet/video/vga_output/__init__.py @@ -203,3 +203,8 @@ def build(self, target, args, test_pattern=True): async def run(self, device, args): return await device.demultiplexer.claim_interface(self, self.mux_interface, args) + + @classmethod + def tests(cls): + from . import test + return test.VGAOutputAppletTestCase diff --git a/software/glasgow/applet/video/ws2812_output/__init__.py b/software/glasgow/applet/video/ws2812_output/__init__.py index 95183dd6c..ac70fa700 100644 --- a/software/glasgow/applet/video/ws2812_output/__init__.py +++ b/software/glasgow/applet/video/ws2812_output/__init__.py @@ -209,3 +209,8 @@ async def interact(self, device, args, leds): await leds.flush(wait=False) except asyncio.CancelledError: pass + + @classmethod + def tests(cls): + from . import test + return test.VideoWS2812OutputAppletTestCase diff --git a/software/glasgow/cli.py b/software/glasgow/cli.py index 4649217c9..5ff5ccac7 100644 --- a/software/glasgow/cli.py +++ b/software/glasgow/cli.py @@ -149,7 +149,7 @@ def add_applet_arg(parser, mode, required=False): applet_cls = metadata.applet_cls - if mode == "test" and not hasattr(applet_cls, "test_cls"): + if mode == "test" and applet_cls.tests() is None: continue if mode == "tool" and not hasattr(applet_cls, "tool_cls"): continue @@ -827,11 +827,11 @@ def startTest(test): result.stream.write("\n") result.startTest = startTest if args.tests == []: - suite = loader.loadTestsFromTestCase(applet.test_cls) + suite = loader.loadTestsFromTestCase(applet.tests()) suite.run(result) else: for test in args.tests: - suite = loader.loadTestsFromName(test, module=applet.test_cls) + suite = loader.loadTestsFromName(test, module=applet.tests()) suite.run(result) if not result.wasSuccessful(): for _, traceback in result.errors + result.failures: