#!/usr/bin/env python3 """ Convert an Intel HEX file to a Verilog ROM module using Amaranth. Usage: python3 hex2rom_amaranth.py program.hex > prog_rom.v Produces a module 'prog_rom' with: input clk, input [12:0] addr, output [13:0] data Depth defaults to 4096 (8192/2). """ import sys import argparse from amaranth import * from amaranth.back import verilog DEPTH = 8192 // 2 DATA_WIDTH = 14 ADDR_WIDTH = 13 # external addr width def parse_ihex(path): words = [0] * DEPTH with open(path) as f: for line in f: line = line.strip() if not line.startswith(':'): continue byte_count = int(line[1:3], 16) address = int(line[3:7], 16) record_type = int(line[7:9], 16) if record_type != 0: continue data = bytes.fromhex(line[9:9 + byte_count * 2]) for i in range(0, len(data) - 1, 2): word_addr = (address + i) // 2 if 0 <= word_addr < DEPTH: words[word_addr] = (data[i] | (data[i+1] << 8)) & ((1 << DATA_WIDTH) - 1) return words def make_rom_module(words): m = Module() clk = Signal() addr = Signal(ADDR_WIDTH) data = Signal(DATA_WIDTH) mem = Memory(width=DATA_WIDTH, depth=DEPTH, init=words) rd = mem.read_port(transparent=False) m.submodules += rd m.d.comb += [ rd.addr.eq(addr), data.eq(rd.data) ] return m, clk, addr, data def render_verilog(words, module_name="prog_rom"): m, clk, addr, data = make_rom_module(words) v = verilog.convert(m, ports=[clk, addr, data], name=module_name) return v.replace("(* top = 1 *)\n", "(* top = 0 *)\n") class ComponentWrapper(Elaboratable): def __init__(self, module_obj, ports, name="prog_rom"): self._module = module_obj self._ports = ports self._name = name def elaborate(self, platform=None): return self._module def name(self): return self._name if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('hexfile') parser.add_argument('--module-name', default='prog_rom') args = parser.parse_args() words = parse_ihex(args.hexfile) v = render_verilog(words, module_name=args.module_name) sys.stdout.write(v)