2023-04-22 09:10:53 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
|
|
# Base on Nexus cells_xtra.py
|
|
|
|
|
|
|
|
|
|
from argparse import ArgumentParser
|
|
|
|
|
import os.path
|
|
|
|
|
from enum import Enum, auto
|
|
|
|
|
import sys
|
|
|
|
|
import re
|
|
|
|
|
|
|
|
|
|
class State(Enum):
|
|
|
|
|
OUTSIDE = auto()
|
|
|
|
|
IN_MODULE = auto()
|
2025-07-02 04:39:18 +02:00
|
|
|
IN_MODULE_MULTILINE = auto()
|
2023-04-22 09:10:53 +02:00
|
|
|
IN_PARAMETER = auto()
|
|
|
|
|
|
2025-03-27 21:34:26 +01:00
|
|
|
_skip = { # These are already described, no need to extract them from the vendor files
|
|
|
|
|
'ALU', 'BANDGAP', 'DFF', 'DFFC', 'DFFCE', 'DFFE', 'DFFN', 'DFFNC', 'DFFNCE',
|
2023-04-22 09:10:53 +02:00
|
|
|
'DFFNE', 'DFFNP', 'DFFNPE', 'DFFNR', 'DFFNRE', 'DFFNS', 'DFFNSE',
|
|
|
|
|
'DFFP', 'DFFPE', 'DFFR', 'DFFRE', 'DFFS', 'DFFSE', 'DP', 'DPX9',
|
|
|
|
|
'ELVDS_OBUF', 'GND', 'GSR', 'IBUF', 'IDDR', 'IDDRC', 'IDES10',
|
|
|
|
|
'IDES16', 'IDES4', 'IDES8', 'IOBUF', 'IVIDEO', 'LUT1', 'LUT2',
|
|
|
|
|
'LUT3', 'LUT4', 'MUX2', 'MUX2_LUT5', 'MUX2_LUT6', 'MUX2_LUT7',
|
|
|
|
|
'MUX2_LUT8', 'OBUF', 'ODDR', 'ODDRC', 'OSC', 'OSCF', 'OSCH',
|
|
|
|
|
'OSCO', 'OSCW', 'OSCZ', 'OSER10', 'OSER16', 'OSER10', 'OSER4',
|
|
|
|
|
'OSER8', 'OVIDEO', 'PLLVR', 'RAM16S1', 'RAM16S2', 'RAM16S4',
|
|
|
|
|
'RAM16SDP1', 'RAM16SDP2', 'RAM16SDP4', 'rPLL', 'SDP',
|
Gowin. Renaming inputs of the DCS primitive.
The dynamic clock selection (DCS) primitive has undergone changes with
the release of the GW5A series—the CLK0,1,2,3 inputs are now
CLKIN0,1,2,3, but only for GW5A series chips.
There are no functional changes, only renaming.
Here we are transferring the description of the DCS primitive from
general to specialized files for each chip series.
We have also fixed a bug in the generation script that caused the loss
of primitive parameters. Fortunately, this only affected the
analog-to-digital converter, which has not yet been implemented.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-09-20 03:01:25 +02:00
|
|
|
'SDPX9', 'SP', 'SPX9', 'TBUF', 'TLVDS_OBUF', 'VCC', 'EMCU',
|
2025-03-27 21:34:26 +01:00
|
|
|
# These are not planned for implementation
|
|
|
|
|
'MUX2_MUX8', 'MUX2_MUX16', 'MUX2_MUX32', 'MUX4', 'MUX8', 'MUX16',
|
|
|
|
|
'MUX32', 'DL', 'DLE', 'DLC', 'DLCE', 'DLP', 'DLPE', 'DLN', 'DLNE',
|
|
|
|
|
'DLNC', 'DLNCE', 'DLNP', 'DLNPE', 'rSDP', 'rSDPX9', 'rROM', 'rROMX9',
|
|
|
|
|
'TLVDS_OEN_BK', 'DLL', 'DCC', 'I3C', 'IODELAYA', 'IODELAYC', 'IODELAYB',
|
|
|
|
|
'SPMI', 'PLLO', 'DCCG', 'MIPI_DPHY_RX', 'CLKDIVG', 'PWRGRD', 'FLASH96KA',
|
2025-10-29 03:48:21 +01:00
|
|
|
# ADCs are in a separate file
|
|
|
|
|
'ADCLRC', 'ADCULC', 'ADC', 'ADC_SAR', 'ADCA',
|
2023-04-22 09:10:53 +02:00
|
|
|
}
|
|
|
|
|
def xtract_cells_decl(dir, fout):
|
|
|
|
|
fname = os.path.join(dir, 'prim_sim.v')
|
|
|
|
|
with open(fname) as f:
|
|
|
|
|
state = State.OUTSIDE
|
|
|
|
|
for l in f:
|
|
|
|
|
l, _, comment = l.partition('//')
|
|
|
|
|
if l.startswith("module "):
|
|
|
|
|
cell_name = l[7:l.find('(')].strip()
|
|
|
|
|
if cell_name not in _skip:
|
|
|
|
|
state = State.IN_MODULE
|
|
|
|
|
fout.write(f'\nmodule {cell_name} (...);\n')
|
|
|
|
|
elif l.startswith(('input', 'output', 'inout')) and state == State.IN_MODULE:
|
|
|
|
|
fout.write(l)
|
|
|
|
|
if l[-1] != '\n':
|
|
|
|
|
fout.write('\n')
|
2025-07-02 04:39:18 +02:00
|
|
|
if l.rstrip()[-1] != ';':
|
|
|
|
|
state = State.IN_MODULE_MULTILINE
|
Gowin. Renaming inputs of the DCS primitive.
The dynamic clock selection (DCS) primitive has undergone changes with
the release of the GW5A series—the CLK0,1,2,3 inputs are now
CLKIN0,1,2,3, but only for GW5A series chips.
There are no functional changes, only renaming.
Here we are transferring the description of the DCS primitive from
general to specialized files for each chip series.
We have also fixed a bug in the generation script that caused the loss
of primitive parameters. Fortunately, this only affected the
analog-to-digital converter, which has not yet been implemented.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-09-20 03:01:25 +02:00
|
|
|
elif l.lstrip().startswith('parameter') and state == State.IN_MODULE:
|
2023-04-22 09:10:53 +02:00
|
|
|
fout.write(l)
|
|
|
|
|
if l.rstrip()[-1] == ',':
|
|
|
|
|
state = State.IN_PARAMETER
|
|
|
|
|
if l[-1] != '\n':
|
|
|
|
|
fout.write('\n')
|
2025-07-02 04:39:18 +02:00
|
|
|
elif l and state == State.IN_MODULE_MULTILINE:
|
|
|
|
|
fout.write(l)
|
|
|
|
|
if l[-1] != '\n':
|
|
|
|
|
fout.write('\n')
|
|
|
|
|
if l.rstrip()[-1] == ';':
|
|
|
|
|
state = State.IN_MODULE
|
2023-04-22 09:10:53 +02:00
|
|
|
elif state == State.IN_PARAMETER:
|
|
|
|
|
fout.write(l)
|
|
|
|
|
if l.rstrip()[-1] == ';':
|
|
|
|
|
state = State.IN_MODULE
|
|
|
|
|
if l[-1] != '\n':
|
|
|
|
|
fout.write('\n')
|
|
|
|
|
elif l.startswith('endmodule') and state == State.IN_MODULE:
|
|
|
|
|
state = State.OUTSIDE
|
|
|
|
|
fout.write('endmodule\n')
|
|
|
|
|
if l[-1] != '\n':
|
|
|
|
|
fout.write('\n')
|
|
|
|
|
|
2025-07-02 04:39:18 +02:00
|
|
|
|
2023-04-22 09:10:53 +02:00
|
|
|
if __name__ == '__main__':
|
|
|
|
|
parser = ArgumentParser(description='Extract Gowin blackbox cell definitions.')
|
|
|
|
|
parser.add_argument('gowin_dir', nargs='?', default='/opt/gowin/')
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
2024-11-26 15:42:22 +01:00
|
|
|
families = {
|
|
|
|
|
'gw1n': os.path.join(args.gowin_dir, 'IDE/simlib/gw1n/'),
|
|
|
|
|
'gw2a': os.path.join(args.gowin_dir, 'IDE/simlib/gw2a/'),
|
|
|
|
|
'gw5a': os.path.join(args.gowin_dir, 'IDE/simlib/gw5a/'),
|
|
|
|
|
}
|
2023-04-22 09:10:53 +02:00
|
|
|
|
2024-11-26 15:42:22 +01:00
|
|
|
for family, dir in families.items():
|
|
|
|
|
if not os.path.isdir(dir):
|
|
|
|
|
print(f'{dir} is not a directory')
|
|
|
|
|
else:
|
|
|
|
|
with open(f'cells_xtra_{family}.v', 'w') as fout:
|
|
|
|
|
fout.write('// Created by cells_xtra.py\n')
|
|
|
|
|
fout.write('\n')
|
|
|
|
|
xtract_cells_decl(dir, fout)
|
2025-10-29 03:48:21 +01:00
|
|
|
if family == 'gw5a':
|
|
|
|
|
fout.write('\n')
|
|
|
|
|
fout.write('// Added from adc.v\n')
|
|
|
|
|
fout.write('\n')
|
|
|
|
|
with open(f'adc.v', 'r') as fin:
|
|
|
|
|
for l in fin:
|
|
|
|
|
fout.write(l);
|
|
|
|
|
|