diff --git a/.github/workflows/chipdb.yml b/.github/workflows/chipdb.yml index 93b75e81..b223b9e0 100644 --- a/.github/workflows/chipdb.yml +++ b/.github/workflows/chipdb.yml @@ -11,6 +11,19 @@ on: pull_request: jobs: + family_info: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build family_info.json + run: | + docker pull pepijndevos/apicula:1.9.8 + docker run -v $(pwd):/usr/src/apicula pepijndevos/apicula:1.9.8 make apycula/family_info.json + - name: Archive artifact + uses: actions/upload-artifact@v3 + with: + name: family_info.json + path: apycula/family_info.json gw1n1: runs-on: ubuntu-latest steps: diff --git a/Makefile b/Makefile index 6a44c478..b635966f 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,8 @@ endif .PHONY: all clean all: apycula/GW1N-1.pickle apycula/GW1N-9.pickle apycula/GW1N-4.pickle \ apycula/GW1NS-2.pickle apycula/GW1NS-4.pickle apycula/GW1N-9C.pickle \ - apycula/GW1NZ-1.pickle apycula/GW2A-18.pickle apycula/GW2A-18C.pickle + apycula/GW1NZ-1.pickle apycula/GW2A-18.pickle apycula/GW2A-18C.pickle \ + family_info.json %.json: apycula/dat19_h4x.py python3 -m apycula.dat19_h4x $* @@ -20,7 +21,12 @@ all: apycula/GW1N-1.pickle apycula/GW1N-9.pickle apycula/GW1N-4.pickle \ apycula/%.pickle: %_stage2.pickle gzip -c $< > $@ +apycula/family_info.json: apycula/build_family_info.py + python3 -m apycula.build_family_info + cp family_info.json apycula/ + clean: rm *.json rm *.pickle rm apycula/*.pickle + rm apycula/family_info.json diff --git a/apycula/build_family_info.py b/apycula/build_family_info.py new file mode 100644 index 00000000..97946d85 --- /dev/null +++ b/apycula/build_family_info.py @@ -0,0 +1,48 @@ +import json +import os +import subprocess +from collections import defaultdict + +def build_family_aliases(): + db_hashes = defaultdict(list) + + family_info = defaultdict(dict) + + # load IDCODEs from programmer.json + with open("/usr/src/gowin/IDE/bin/programmer.json") as f: + data = json.loads(f.read()) + for device_data in data["DEVICE_CONFIG"]: + if device_data["DEVICE"] == "JTAG_NOP": + continue + + family_info[device_data["DEVICE"]]["idcode"] = device_data["IDCODE"] + + # if devices have the same *.fse hash, assume they are the same family + md5_exec = subprocess.run("md5sum /usr/src/gowin/IDE/share/device/*/*.fse", + shell=True, check=True, capture_output=True, + encoding='utf-8') + + for line in md5_exec.stdout.splitlines(): + computed_hash, path = line.split(" ") + family = os.path.split(path)[-1].replace(".fse", "") + db_hashes[computed_hash].append(family) + + for computed_hash, families in db_hashes.items(): + # arbitrarily use the shortest family as the "true" family + families.sort(key=lambda x: (len(x), x)) + + for family in families: + base_family = families[0] + family_info[family]["base_family"] = base_family + + # if the family has no IDCODE, try to inherit from the base family + if "idcode" not in family_info[family] and "idcode" in family_info[base_family]: + family_info[family]["idcode"] = family_info[families[0]]["idcode"] + + return family_info + + +if __name__ == "__main__": + family_aliases = build_family_aliases() + with open("family_info.json", "w") as f: + f.write(json.dumps(dict(family_aliases), indent=2)) diff --git a/apycula/family_aliases.py b/apycula/family_aliases.py new file mode 100644 index 00000000..82598a75 --- /dev/null +++ b/apycula/family_aliases.py @@ -0,0 +1,13 @@ +import importlib.resources +import json + +def replace_family_alias(family): + with importlib.resources.path('apycula', f'family_info.json') as path: + with open(path, 'r') as f: + family_info = json.load(f) + + if family in family_info: + return family_info[family]["base_family"] + else: + return family + diff --git a/apycula/gowin_pack.py b/apycula/gowin_pack.py index c3d3d859..faf6e50d 100644 --- a/apycula/gowin_pack.py +++ b/apycula/gowin_pack.py @@ -17,6 +17,7 @@ from apycula import attrids from apycula import bslib from apycula.wirenames import wirenames, wirenumbers +from apycula.family_aliases import replace_family_alias device = "" pnr = None @@ -1029,12 +1030,25 @@ def route(db, tilemap, pips): for row, col in bits: tile[row][col] = 1 -def header_footer(db, bs, compress): +def header_footer(db, bs, compress, family): """ Generate fs header and footer Currently limited to checksum with CRC_check and security_bit_enable set """ + # override part IDCODE in header + assert db.cmd_hdr[3][0] == 0x06 # IDCODE data + + with importlib.resources.path('apycula', f'family_info.json') as path: + with open(path, 'r') as f: + family_info = json.load(f) + + if family in family_info and "idcode" in family_info[family]: + db.cmd_hdr[3][-4:] = bytes.fromhex(family_info[family]["idcode"]) + else: + print("Not overriding IDCODE as it is not present in family_info.json") + + # calculate checksum bs = np.fliplr(bs) bs=np.packbits(bs) # configuration data checksum is computed on all @@ -1157,7 +1171,7 @@ def main(): parser.add_argument('--png') args = parser.parse_args() - device = args.device + device = replace_family_alias(args.device) with open(args.netlist) as f: pnr = json.load(f) @@ -1173,7 +1187,9 @@ def main(): mods = m.group(1) or "" luts = m.group(3) device = f"GW1N{mods}-{luts}" - with importlib.resources.path('apycula', f'{args.device}.pickle') as path: + device = replace_family_alias(device) + + with importlib.resources.path('apycula', f'{device}.pickle') as path: with closing(gzip.open(path, 'rb')) as f: db = pickle.load(f) @@ -1206,7 +1222,7 @@ def main(): tile[row][col] = 0 res = chipdb.fuse_bitmap(db, tilemap) - header_footer(db, res, args.compress) + header_footer(db, res, args.compress, device) if pil_available and args.png: bslib.display(args.png, res) bslib.write_bitstream(args.output, res, db.cmd_hdr, db.cmd_ftr, args.compress) diff --git a/apycula/gowin_unpack.py b/apycula/gowin_unpack.py index b8566f75..bc2ec623 100644 --- a/apycula/gowin_unpack.py +++ b/apycula/gowin_unpack.py @@ -14,6 +14,7 @@ from apycula import attrids from apycula.bslib import read_bitstream from apycula.wirenames import wirenames +from apycula.family_aliases import replace_family_alias _device = "" _pinout = "" @@ -1074,7 +1075,7 @@ def main(): args = parser.parse_args() global _device - _device = args.device + _device = replace_family_alias(args.device) # For tool integration it is allowed to pass a full part number m = re.match("GW1N(S?)[A-Z]*-(LV|UV|UX)([0-9])C?([A-Z]{2}[0-9]+P?)(C[0-9]/I[0-9])", _device) if m: diff --git a/examples/Makefile b/examples/Makefile index 27d024c1..331a64aa 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -121,16 +121,16 @@ pll-tangnano4k.json: pll-tangnano4k-synth.json tangnano4k.cst $(NEXTPNR) --json $< --write $@ --device GW1NSR-LV4CQN48PC7/I6 --cst tangnano4k.cst %-tangnano9k.fs: %-tangnano9k.json - gowin_pack -d GW1N-9C -o $@ $^ + gowin_pack -d GW1NR-9C -o $@ $^ pll-nanolcd-tangnano9k.fs: pll-nanolcd-tangnano9k.json - gowin_pack -d GW1N-9C --sspi_as_gpio --mspi_as_gpio -o $@ $^ + gowin_pack -d GW1NR-9C --sspi_as_gpio --mspi_as_gpio -o $@ $^ %-tangnano9k.json: %-tangnano9k-synth.json tangnano9k.cst - $(NEXTPNR) --json $< --write $@ --device GW1NR-LV9QN88PC6/I5 --family GW1N-9C --cst tangnano9k.cst + $(NEXTPNR) --json $< --write $@ --device GW1NR-LV9QN88PC6/I5 --family GW1NR-9C --cst tangnano9k.cst %lvds-tangnano9k.json: %lvds-tangnano9k-synth.json lvds-tangnano9k.cst - $(NEXTPNR) --json $< --write $@ --device GW1NR-LV9QN88PC6/I5 --family GW1N-9C --cst lvds-tangnano9k.cst + $(NEXTPNR) --json $< --write $@ --device GW1NR-LV9QN88PC6/I5 --family GW1NR-9C --cst lvds-tangnano9k.cst %-honeycomb.fs: %-honeycomb.json gowin_pack -d GW1NS-2 -o $@ $< @@ -264,7 +264,7 @@ pll-%-honeycomb-synth.json: pll/GW1NS-2C-%.vh pll.v pll/rpll.v $(NEXTPNR) --json $< --write $@ --device GW1NSR-LV4CQN48PC7/I6 --cst longwires/tangnano4k.cst %-lw-tangnano9k.json: %-tangnano9k-synth.json longwires/tangnano9k.cst - $(NEXTPNR) --json $< --write $@ --device GW1NR-LV9QN88PC6/I5 --family GW1N-9C --cst longwires/tangnano9k.cst + $(NEXTPNR) --json $< --write $@ --device GW1NR-LV9QN88PC6/I5 --family GW1NR-9C --cst longwires/tangnano9k.cst %-lw-runber.json: %-runber-synth.json longwires/runber.cst $(NEXTPNR) --json $< --write $@ --device GW1N-UV4LQ144C6/I5 --cst longwires/runber.cst diff --git a/examples/himbaechel/Makefile.himbaechel b/examples/himbaechel/Makefile.himbaechel index 6b6fa623..424232d6 100644 --- a/examples/himbaechel/Makefile.himbaechel +++ b/examples/himbaechel/Makefile.himbaechel @@ -141,10 +141,10 @@ clean: # ============================================================ # Tangnano20k %-tangnano20k.fs: %-tangnano20k.json - gowin_pack -d GW2A-18C -o $@ $< + gowin_pack -d GW2AR-18C -o $@ $< %-tangnano20k.json: %-tangnano20k-synth.json tangnano20k.cst - $(NEXTPNR) --json $< --write $@ --device GW2AR-LV18QN88C8/I7 --vopt family=GW2A-18C --vopt cst=tangnano20k.cst + $(NEXTPNR) --json $< --write $@ --device GW2AR-LV18QN88C8/I7 --vopt family=GW2AR-18C --vopt cst=tangnano20k.cst %-tangnano20k-synth.json: %.v $(YOSYS) -D LEDS_NR=6 -D OSC_TYPE_OSC -D INV_BTN=1 -p "read_verilog $^; synth_gowin -json $@" @@ -223,7 +223,7 @@ blinky-pll-tangnano4k-synth.json: pll/GW1NS-4-dyn.vh blinky-pll-vr.v gowin_pack -d GW1N-9C -o $@ $^ %-tangnano9k.json: %-tangnano9k-synth.json tangnano9k.cst - $(NEXTPNR) --json $< --write $@ --device GW1NR-LV9QN88PC6/I5 --vopt family=GW1N-9C --vopt cst=tangnano9k.cst + $(NEXTPNR) --json $< --write $@ --device GW1NR-LV9QN88PC6/I5 --vopt family=GW1NR-9C --vopt cst=tangnano9k.cst %-tangnano9k-synth.json: %.v $(YOSYS) -D LEDS_NR=6 -D OSC_TYPE_OSC -D INV_BTN=0 -p "read_verilog $^; synth_gowin -json $@" @@ -232,7 +232,7 @@ pll-nanolcd-tangnano9k-synth.json: pll/GW1N-9C-dyn.vh pll-nanolcd/TOP.v pll-nano $(YOSYS) -D INV_BTN=0 -p "read_verilog $^; synth_gowin -json $@" pll-nanolcd-tangnano9k.fs: pll-nanolcd-tangnano9k.json - gowin_pack -d GW1N-9C --sspi_as_gpio --mspi_as_gpio -o $@ $^ + gowin_pack -d GW1NR-9C --sspi_as_gpio --mspi_as_gpio -o $@ $^ # ============================================================ # szfpga (GW1N-9) @@ -288,10 +288,10 @@ blinky-pll-runber-synth.json: pll/GW1N-4-dyn.vh blinky-pll.v gowin_unpack -d GW1NS-4 -o $@ $^ %-tangnano9k-unpacked.v: %-tangnano9k.fs - gowin_unpack -d GW1N-9C -o $@ $^ + gowin_unpack -d GW1NR-9C -o $@ $^ %-tangnano20k-unpacked.v: %-tangnano20k.fs - gowin_unpack -d GW2A-18C -o $@ $^ + gowin_unpack -d GW2AR-18C -o $@ $^ %-runber-unpacked.v: %-runber.fs gowin_unpack -d GW1N-4 -o $@ $^