From 9398cd18ed9e35bd0db8633e5574b0a90bc222c1 Mon Sep 17 00:00:00 2001 From: Keith Penney Date: Thu, 30 Jan 2025 10:56:33 -0800 Subject: [PATCH] Adding i2cbridge and marble_i2c regression tests to CI --- .gitlab/ci/marble_family.gitlab-ci.yml | 4 ++ peripheral_drivers/i2cbridge/decode.py | 4 +- projects/test_marble_family/i2c/Makefile | 18 ++++++-- projects/test_marble_family/i2c/README.md | 2 +- .../test_marble_family/i2c/demo_i2c_baddy.py | 4 +- .../test_marble_family/i2c/demo_marble_i2c.py | 23 ++++++++-- projects/test_marble_family/i2c/marble_i2c.py | 46 ++++++++++++++----- .../i2c/marble_i2c_decoder.py | 8 ++-- 8 files changed, 80 insertions(+), 29 deletions(-) diff --git a/.gitlab/ci/marble_family.gitlab-ci.yml b/.gitlab/ci/marble_family.gitlab-ci.yml index bfa8cd315..723384b3f 100644 --- a/.gitlab/ci/marble_family.gitlab-ci.yml +++ b/.gitlab/ci/marble_family.gitlab-ci.yml @@ -58,3 +58,7 @@ marble_run: script: - make marble2_hwload SERIAL=39 && sleep 8 && make marble2_hwtest SERIAL=39 +marble_i2c_check: + stage: test + script: + - make -C projects/test_marble_family/i2c tests diff --git a/peripheral_drivers/i2cbridge/decode.py b/peripheral_drivers/i2cbridge/decode.py index d733725b6..eac78317a 100644 --- a/peripheral_drivers/i2cbridge/decode.py +++ b/peripheral_drivers/i2cbridge/decode.py @@ -182,7 +182,7 @@ def decode(self, prog): pa += 1 self.print("---- End of report ----") - return + return 0 def load_file(file_path=None, base=None): @@ -241,4 +241,4 @@ def main(): if __name__ == '__main__': - main() + exit(main()) diff --git a/projects/test_marble_family/i2c/Makefile b/projects/test_marble_family/i2c/Makefile index 9616b851e..e436d5c20 100644 --- a/projects/test_marble_family/i2c/Makefile +++ b/projects/test_marble_family/i2c/Makefile @@ -1,16 +1,24 @@ # A demo of Marble-specific I2C functionality -PYTHON = python3 +PYTHON ?= python3 TARGETS = prog.dat prog.vh prog.h prog.json .PHONY: all all: $(TARGETS) -# ======= Build a program for i2c_chunk +.PHONY: tests +tests: check violations decode generic_decode + +# ======= Simply build and check a program PROG_FILE = demo_marble_i2c.py +.PHONY: check +check: $(PROG_FILE) + $(PYTHON) $< + +# ======= Build a program for i2c_chunk prog.dat: $(PROG_FILE) - $(PYTHON) $< > $@ + $(PYTHON) $< p > $@ # ======= Build a memory map in Verilog format prog.vh: $(PROG_FILE) @@ -26,8 +34,8 @@ prog.json: $(PROG_FILE) # ======= Test a bunch of assembler violations BAD_FILE = demo_i2c_baddy.py -.PHONY: test -test: $(BAD_FILE) +.PHONY: violations +violations: $(BAD_FILE) $(PYTHON) $< # ======= Decode a program in marble context diff --git a/projects/test_marble_family/i2c/README.md b/projects/test_marble_family/i2c/README.md index a1631c2d7..56c726561 100644 --- a/projects/test_marble_family/i2c/README.md +++ b/projects/test_marble_family/i2c/README.md @@ -50,6 +50,6 @@ i2cbridge tools. Note the difference in readability from the application perspe This last test is not truly platform-specific but shows various violations of the I2C assembler rules using the Marble platform-aware interface. None of the rules violated are specific to the platform. -`make test` +`make violations` See `demo_i2c_baddy.py` for examples of what to avoid. diff --git a/projects/test_marble_family/i2c/demo_i2c_baddy.py b/projects/test_marble_family/i2c/demo_i2c_baddy.py index 5499f2fdf..8b406f4c2 100644 --- a/projects/test_marble_family/i2c/demo_i2c_baddy.py +++ b/projects/test_marble_family/i2c/demo_i2c_baddy.py @@ -46,7 +46,7 @@ def doViolations(argv): violations[n]() except marble_i2c.assem.I2C_Assembler_Exception as i2ce: print(f"{i2ce}\n") - return + return 0 def violation1(): @@ -113,4 +113,4 @@ def violation6(): if __name__ == "__main__": - doViolations(sys.argv) + exit(doViolations(sys.argv)) diff --git a/projects/test_marble_family/i2c/demo_marble_i2c.py b/projects/test_marble_family/i2c/demo_marble_i2c.py index 35c123fbd..6b7ce0baf 100644 --- a/projects/test_marble_family/i2c/demo_marble_i2c.py +++ b/projects/test_marble_family/i2c/demo_marble_i2c.py @@ -109,17 +109,32 @@ def build_prog(argv): m.pause(4096) # Pause for roughly 0.24ms m.jump(jump_n) # Jump back to loop start - # ======= End Program ======= + # ======= Demo Functionality ======= if len(argv) > 1: op = argv[1] if len(argv) > 2: offset = _int(argv[2]) else: offset = 0 - m.write_reg_map(offset=offset, style=op) + if op == 'p': + m.write_program() + else: + m.write_reg_map(offset=offset, style=op) + return 0 + + # ======= (Anti-)Regression Test ======= + rval = 0 + errstr = None + try: + m.check_program() + except marble_i2c.assem.I2C_Assembler_Exception as err: + rval = 1 + errstr = str(err) + if rval == 0: + print("PASS") else: - m.write_program() - return + print("FAIL: {}".format(errstr)) + return rval if __name__ == "__main__": diff --git a/projects/test_marble_family/i2c/marble_i2c.py b/projects/test_marble_family/i2c/marble_i2c.py index 16ccb8150..857d8a47d 100644 --- a/projects/test_marble_family/i2c/marble_i2c.py +++ b/projects/test_marble_family/i2c/marble_i2c.py @@ -68,13 +68,13 @@ class MarbleI2C(): _ina219_map = {0: "U17", 1: "U32", 2: "U57"} # Build a 2D list from the map - _a = [] + _ic_list = [] for mux, tree in _i2c_map.items(): mux_name, mux_addr = mux for ch, branch in tree.items(): branch_name, branch_dict = branch for ic_name, ic_addr in branch_dict.items(): - _a.append((ic_name, ic_addr, branch_name, ch, mux_name, mux_addr)) + _ic_list.append((ic_name, ic_addr, branch_name, ch, mux_name, mux_addr)) # ========= IC-Specific Information ================= # U34 (PCAL9555) I2C GPIO expander @@ -172,17 +172,41 @@ def _inherit(self): def _associate(self): """[private] Build a 2D list from the map""" - if hasattr(self, "_a"): + if hasattr(self, "_ic_list"): return - self._a = [] + self._ic_list = [] for mux, tree in self._i2c_map.items(): mux_name, mux_addr = mux for ch, branch in tree.items(): branch_name, branch_dict = branch for ic_name, ic_addr in branch_dict.items(): - self._a.append((ic_name, ic_addr, branch_name, ch, mux_name, mux_addr)) + self._ic_list.append((ic_name, ic_addr, branch_name, ch, mux_name, mux_addr)) return + @property + def ic_list(self): + """Returns list of tuple entries: + (ic_name, ic_addr, branch_name, branch, mux_name, mux_addr) + string ic_name: Refdes (i.e. "U5") from schematic + int ic_addr: I2C address of the IC (in 8-bit format) + string branch_name: The name of the I2C branch the IC lives on (i.e. "APP") + int branch: The index of the branch (how it is selected by the bus mux) + string mux_name: The refdes of the bus mux that leads to this IC. + int mux_addr: The I2C address of the bus mux that leads to this IC. + """ + return self._ic_list + + @property + def ic_dict(self): + """Returns dict of tuple entries: + ic_name: (ic_addr, branch_name, branch, mux_name, mux_addr) + See property 'ic_list' for description of each item. + """ + dd = {} + for entry in self._ic_list: + dd[entry[0]] = entry[1:] + return dd + def _busmux(self, mux_addr, mux_ch): """[private] Select a channel with the bus multiplexer at address mux_addr.""" if self._s is None: @@ -222,7 +246,7 @@ def get_mux_dict(cls, mux_name=None): def get_ics(cls): """Returns a list of tuples (name_str, i2c_address_int) for all ICs in the I2C map.""" ics = [] - for _l in cls._a: + for _l in cls._ic_list: name = _l[0] addr = _l[1] ics.append((name, addr)) @@ -235,7 +259,7 @@ def get_i2c_addr(cls, ic_name): string ic_name: Any valid IC name in the I2C map Returns int I2C address if 'ic_name' is found in the I2C map, otherwise None. """ - for _l in cls._a: + for _l in cls._ic_list: if ic_name == _l[0]: return _l[1] return None @@ -247,7 +271,7 @@ def get_i2c_name(cls, i2c_addr): int i2c_addr: I2C address of the desired IC (0-255). Returns string IC name if 'i2c_addr' is found in the I2C map, otherwise None. """ - for _l in cls._a: + for _l in cls._ic_list: if i2c_addr == _l[1]: return _l[0] return None @@ -264,7 +288,7 @@ def select_ic(self, ic_name): if ic_name == busmux_ic: # We can always talk to the busmux return busmux_addr - for nic in self._a: + for nic in self._ic_list: _ic_name, ic_addr, branch_name, ch, mux_name, mux_addr = nic if ic_name.lower().strip() == _ic_name.lower().strip(): # If we found a match, mux to it @@ -279,7 +303,7 @@ def select_branch(self, branch): Returns int channel number (0-7) that was selected or None if 'branch' does not match any of the above. """ - for nic in self._a: + for nic in self._ic_list: ic_name, ic_addr, branch_name, ch, mux_name, mux_addr = nic if branch_name.lower().strip() == branch.lower().strip(): # If we found a match, mux to it @@ -293,7 +317,7 @@ def get_addr(cls, ic_name): Params: string ic_name: Any valid IC name in the I2C map Returns I2C address (int) if 'ic_name' is found in the I2C map, otherwise None.""" - for nic in cls._a: + for nic in cls._ic_list: _ic_name, ic_addr, branch_name, ch, mux_name, mux_addr = nic if ic_name.lower().strip() == _ic_name.lower().strip(): return ic_addr diff --git a/projects/test_marble_family/i2c/marble_i2c_decoder.py b/projects/test_marble_family/i2c/marble_i2c_decoder.py index 50e844669..5eeb38eac 100644 --- a/projects/test_marble_family/i2c/marble_i2c_decoder.py +++ b/projects/test_marble_family/i2c/marble_i2c_decoder.py @@ -64,7 +64,7 @@ def marble_write(devaddr, nbytes, cmd_table): msg = f"Busmux - bitmask: 0b{bitmask:08b} Selected {seltext}" if msg is None: data = [] - for _l in marble._a: + for _l in marble.ic_list: ic_name, ic_addr, branch_name, ch, mux_name, mux_addr = _l if devaddr == ic_addr and ((1 << ch) & bus_bitmask): for n in range(nbytes): @@ -81,7 +81,7 @@ def marble_read(devaddr, nbytes, cmd_table): for muxname, address in marble.get_muxes(): if devaddr == address: msg = "Busmux - Readback" - for _l in marble._a: + for _l in marble.ic_list: ic_name, ic_addr, branch_name, ch, mux_name, mux_addr = _l if devaddr == ic_addr and ((1 << ch) & bus_bitmask): s = "" if nbytes == 1 else "s" @@ -93,7 +93,7 @@ def marble_write_rpt(devaddr, memaddr, nbytes, cmd_table): data = [] msg = None inc = 0 - for _l in marble._a: + for _l in marble.ic_list: ic_name, ic_addr, branch_name, ch, mux_name, mux_addr = _l if devaddr == ic_addr and ((1 << ch) & bus_bitmask): if nbytes == 0: @@ -113,4 +113,4 @@ def marble_write_rpt(devaddr, memaddr, nbytes, cmd_table): # ============================================================================= if __name__ == "__main__": - _decode.main() + exit(_decode.main())