2023-06-28 02:16:29 +02:00
from os import path
import sys
import importlib . resources
import pickle
import gzip
2023-06-28 08:32:53 +02:00
import re
2023-06-28 02:16:29 +02:00
import argparse
sys . path . append ( path . join ( path . dirname ( __file__ ) , " ../.. " ) )
from himbaechel_dbgen . chip import *
from apycula import chipdb
2023-07-25 05:25:33 +02:00
# Bel flags
BEL_FLAG_SIMPLE_IO = 0x100
2025-09-02 07:51:08 +02:00
# Wire flags
WIRE_FLAG_CLOCK_GATE = 0x1
gowin: Himbaechel. Add BSRAM for all chips.
The following primitives are implemented for the GW1N-1, GW2A-18,
GW2AR-18C, GW1NSR-4C, GW1NR-9C, GW1NR-9 and GW1N-4 chips:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
For GW1NSR-4C and GW1NR-9 chips, SP/SPX9 primitives with data widths
of 32/36 bits are implemented using a pair of 16-bit wide
primitives.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-26 11:51:16 +01:00
# Chip flags
2024-06-28 00:15:50 +02:00
CHIP_HAS_SP32 = 0x1
CHIP_NEED_SP_FIX = 0x2
2024-06-25 10:27:00 +02:00
CHIP_NEED_BSRAM_OUTREG_FIX = 0x4
2024-06-28 00:15:50 +02:00
CHIP_NEED_BLKSEL_FIX = 0x8
2024-07-06 11:14:43 +02:00
CHIP_HAS_BANDGAP = 0x10
2025-03-12 09:32:38 +01:00
CHIP_HAS_PLL_HCLK = 0x20
CHIP_HAS_CLKDIV_HCLK = 0x40
2025-08-07 23:10:18 +02:00
CHIP_HAS_PINCFG = 0x80
2025-08-14 13:19:39 +02:00
CHIP_HAS_DFF67 = 0x100
2025-08-26 16:17:55 +02:00
CHIP_HAS_CIN_MUX = 0x200
2025-10-04 15:03:33 +02:00
CHIP_NEED_BSRAM_RESET_FIX = 0x400
Gowin. Add BSRAM SDP fix. (#1575)
In the GW5A series, the primitive SemiDual Port BSRAM cannot function
when the width of any of the ports is 32/36 bits - it is necessary to
divide one block into two identical ones, each of which will be
responsible for 16 bits.
Here, we perform such a division and, in addition, ensure that the new
cells resulting from the division undergo the same packing procedure as
the original ones.
Naturally, with some reservations (the AUX attribute is responsible for
this) - in the case of SP, when service elements are added, it makes
sense to do this immediately for 32-bit SP and only then divide.
Also, SDPs are currently being corrected for cases where both ports are
‘problematic’, but it may happen that one port is 32 and the other is,
say, 1/2/4/8/16. This has been left for the future.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-10-13 11:07:39 +02:00
CHIP_NEED_SDP_FIX = 0x800
gowin: Himbaechel. Add BSRAM for all chips.
The following primitives are implemented for the GW1N-1, GW2A-18,
GW2AR-18C, GW1NSR-4C, GW1NR-9C, GW1NR-9 and GW1N-4 chips:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
For GW1NSR-4C and GW1NR-9 chips, SP/SPX9 primitives with data widths
of 32/36 bits are implemented using a pair of 16-bit wide
primitives.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-26 11:51:16 +01:00
2025-01-29 14:02:21 +01:00
# Tile flags
TILE_I3C_CAPABLE_IO = 0x1
2023-06-30 09:18:14 +02:00
# Z of the bels
2023-07-02 08:09:39 +02:00
# sync with C++ part!
LUT0_Z = 0 # z(DFFx) = z(LUTx) + 1
LUT7_Z = 14
MUX20_Z = 16
MUX21_Z = 18
MUX23_Z = 22
MUX27_Z = 29
2023-07-05 04:49:25 +02:00
ALU0_Z = 30 # : 35, 6 ALUs
2023-07-06 06:48:44 +02:00
RAMW_Z = 36 # RAM16SDP4
2023-07-02 08:09:39 +02:00
2023-07-23 08:46:04 +02:00
IOBA_Z = 50
IOBB_Z = 51
2023-08-06 12:56:08 +02:00
IOLOGICA_Z = 70
2024-02-09 08:44:57 +01:00
IDES16_Z = 74
OSER16_Z = 75
2023-08-06 12:56:08 +02:00
2024-02-09 08:44:57 +01:00
BUFG_Z = 76 # : 81 reserve just in case
2023-10-03 13:11:40 +02:00
BSRAM_Z = 100
2023-09-04 14:20:08 +02:00
2023-08-10 13:24:30 +02:00
OSC_Z = 274
2023-07-22 02:01:35 +02:00
PLL_Z = 275
2023-07-19 05:29:18 +02:00
GSR_Z = 276
2023-07-02 08:09:39 +02:00
VCC_Z = 277
GND_Z = 278
2024-07-06 11:14:43 +02:00
BANDGAP_Z = 279
2023-07-02 08:09:39 +02:00
2024-07-14 08:53:26 +02:00
DQCE_Z = 280 # : 286 reserve for 6 DQCEs
2024-09-04 12:55:35 +02:00
DCS_Z = 286 # : 288 reserve for 2 DCSs
2024-09-11 11:18:26 +02:00
DHCEN_Z = 288 # : 298
USERFLASH_Z = 298
2024-09-04 12:55:35 +02:00
2024-09-12 09:53:39 +02:00
EMCU_Z = 300
2025-01-23 10:17:31 +01:00
MIPIOBUF_Z = 301
MIPIIBUF_Z = 302
2025-03-19 08:41:35 +01:00
DLLDLY_Z = 303 # : 305 reserve for 2 DLLDLYs
2025-11-06 09:17:05 +01:00
PINCFG_Z = 400
ADC_Z = 401
2025-08-07 23:10:18 +02:00
2024-03-18 13:08:52 +01:00
DSP_Z = 509
DSP_0_Z = 511 # DSP macro 0
PADD18_0_0_Z = 512
PADD9_0_0_Z = 512 + 1
PADD9_0_1_Z = 512 + 2
PADD18_0_1_Z = 516
PADD9_0_2_Z = 516 + 1
PADD9_0_3_Z = 516 + 2
MULT18X18_0_0_Z = 520
MULT9X9_0_0_Z = 520 + 1
MULT9X9_0_1_Z = 520 + 2
MULT18X18_0_1_Z = 524
MULT9X9_0_2_Z = 524 + 1
MULT9X9_0_3_Z = 524 + 2
ALU54D_0_Z = 524 + 3
MULTALU18X18_0_Z = 528
MULTALU36X18_0_Z = 528 + 1
MULTADDALU18X18_0_Z = 528 + 2
MULT36X36_Z = 528 + 3
DSP_1_Z = 543 # DSP macro 1
PADD18_1_0_Z = 544
PADD9_1_0_Z = 544 + 1
PADD9_1_1_Z = 544 + 2
PADD18_1_1_Z = 548
PADD9_1_2_Z = 548 + 1
PADD9_1_3_Z = 548 + 2
MULT18X18_1_0_Z = 552
MULT9X9_1_0_Z = 552 + 1
MULT9X9_1_1_Z = 552 + 2
MULT18X18_1_1_Z = 556
MULT9X9_1_2_Z = 556 + 1
MULT9X9_1_3_Z = 556 + 2
ALU54D_1_Z = 556 + 3
MULTALU18X18_1_Z = 560
MULTALU36X18_1_Z = 560 + 1
MULTADDALU18X18_1_Z = 560 + 2
2024-08-03 15:57:22 +02:00
CLKDIV2_0_Z = 610
CLKDIV2_1_Z = 611
CLKDIV2_2_Z = 612
CLKDIV2_3_Z = 613
CLKDIV_0_Z = 620
CLKDIV_1_Z = 621
CLKDIV_2_Z = 622
CLKDIV_3_Z = 623
2023-07-23 08:46:04 +02:00
# =======================================
# Chipdb additional info
# =======================================
2023-07-02 08:09:39 +02:00
@dataclass
class TileExtraData ( BBAStruct ) :
tile_class : IdString # The general functionality of the slightly different tiles,
# let's say the behavior of LUT+DFF in the tiles are completely identical,
# but one of them also contains clock-wire switches,
# then we assign them to the same LOGIC class.
2023-08-13 14:05:18 +02:00
io16_x_off : int = 0 # OSER16/IDES16 offsets to the aux cell
io16_y_off : int = 0
2025-01-29 14:02:21 +01:00
tile_flags : int = 0
2023-08-13 14:05:18 +02:00
2023-07-02 08:09:39 +02:00
def serialise_lists ( self , context : str , bba : BBAWriter ) :
pass
def serialise ( self , context : str , bba : BBAWriter ) :
bba . u32 ( self . tile_class . index )
2023-08-13 14:05:18 +02:00
bba . u16 ( self . io16_x_off )
bba . u16 ( self . io16_y_off )
2025-01-29 14:02:21 +01:00
bba . u32 ( self . tile_flags )
2023-06-30 09:18:14 +02:00
2023-07-23 08:46:04 +02:00
@dataclass
class BottomIOCnd ( BBAStruct ) :
wire_a_net : IdString
wire_b_net : IdString
def serialise_lists ( self , context : str , bba : BBAWriter ) :
pass
def serialise ( self , context : str , bba : BBAWriter ) :
bba . u32 ( self . wire_a_net . index )
bba . u32 ( self . wire_b_net . index )
@dataclass
class BottomIO ( BBAStruct ) :
conditions : list [ BottomIOCnd ] = field ( default_factory = list )
def serialise_lists ( self , context : str , bba : BBAWriter ) :
bba . label ( f " { context } _conditions " )
for i , cnd in enumerate ( self . conditions ) :
cnd . serialise ( f " { context } _cnd { i } " , bba )
def serialise ( self , context : str , bba : BBAWriter ) :
bba . slice ( f " { context } _conditions " , len ( self . conditions ) )
2024-07-14 08:53:26 +02:00
# spine -> bel for different bels
@dataclass
class SpineBel ( BBAStruct ) :
spine : IdString
bel_x : int
bel_y : int
bel_z : int
def serialise_lists ( self , context : str , bba : BBAWriter ) :
pass
def serialise ( self , context : str , bba : BBAWriter ) :
bba . u32 ( self . spine . index )
bba . u32 ( self . bel_x )
bba . u32 ( self . bel_y )
bba . u32 ( self . bel_z )
2025-03-19 08:41:35 +01:00
# io -> dlldly bels
@dataclass
class IoBel ( BBAStruct ) :
io : IdString
dlldly : IdString
def serialise_lists ( self , context : str , bba : BBAWriter ) :
pass
def serialise ( self , context : str , bba : BBAWriter ) :
bba . u32 ( self . io . index )
bba . u32 ( self . dlldly . index )
2024-09-11 11:18:26 +02:00
# wire -> bel for DHCEN bels
@dataclass
class WireBel ( BBAStruct ) :
pip_xy : IdString
pip_dst : IdString
pip_src : IdString
bel_x : int
bel_y : int
bel_z : int
hclk_side : IdString
def serialise_lists ( self , context : str , bba : BBAWriter ) :
pass
def serialise ( self , context : str , bba : BBAWriter ) :
bba . u32 ( self . pip_xy . index )
bba . u32 ( self . pip_dst . index )
bba . u32 ( self . pip_src . index )
bba . u32 ( self . bel_x )
bba . u32 ( self . bel_y )
bba . u32 ( self . bel_z )
bba . u32 ( self . hclk_side . index )
Gowin. Add a router for segments. (#1456)
Gowin chips have an interesting mechanism - wires that run vertically
through several rows (at least 10) in each column of the chip. In each
row a particular wire has branches to the left and right, covering on
average 4 neighboring cells in the row. For lack of a better term, I
further call such a wire a segment.
So a segment can provide a direct connection in a local rectangle. There
are no special restrictions on the sinks, so segment networks can be
used for ClockEnable, LocalSetReset, as well as for LUT and DFF inputs.
The sources are not so simple - the sources can be the upper or lower
end of the segment, which in theory can lead to unfortunate consequences
if the signal is applied from both ends.
The matter is complicated by the fact that there are default
connections, i.e. in the absence of any set fuse the segment input is
still connected to something (VCC for example) and to disable the unused
end of the segment you need to set a special combination of fuses.
Taking into account which end of which segment is used is one of the
tasks of this router. In addition, segment ends can physically coincide
with PLL, DSP and BSRAM inputs, which can also lead to unexpected
effects. Some of these things are tracked when generating the base, some
in this router, some when packing in gowin_pack.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-03-18 12:02:49 +01:00
# segment column description
@dataclass
class Segment ( BBAStruct ) :
x : int
seg_idx : int
min_x : int
min_y : int
max_x : int
max_y : int
top_row : int
bottom_row : int
top_wire : IdString
bottom_wire : IdString
top_gate_wire : list [ IdString ] = field ( default_factory = list )
bottom_gate_wire : list [ IdString ] = field ( default_factory = list )
def serialise_lists ( self , context : str , bba : BBAWriter ) :
bba . label ( f " { context } _top_gate_wire " )
for i , wire in enumerate ( self . top_gate_wire ) :
bba . u32 ( wire . index )
bba . label ( f " { context } _bottom_gate_wire " )
for i , wire in enumerate ( self . bottom_gate_wire ) :
bba . u32 ( wire . index )
def serialise ( self , context : str , bba : BBAWriter ) :
bba . u16 ( self . x )
bba . u16 ( self . seg_idx )
bba . u16 ( self . min_x )
bba . u16 ( self . min_y )
bba . u16 ( self . max_x )
bba . u16 ( self . max_y )
bba . u16 ( self . top_row )
bba . u16 ( self . bottom_row )
bba . u32 ( self . top_wire . index )
bba . u32 ( self . bottom_wire . index )
bba . slice ( f " { context } _top_gate_wire " , len ( self . top_gate_wire ) )
bba . slice ( f " { context } _bottom_gate_wire " , len ( self . bottom_gate_wire ) )
2023-07-23 08:46:04 +02:00
@dataclass
class ChipExtraData ( BBAStruct ) :
strs : StringPool
gowin: Himbaechel. Add BSRAM for all chips.
The following primitives are implemented for the GW1N-1, GW2A-18,
GW2AR-18C, GW1NSR-4C, GW1NR-9C, GW1NR-9 and GW1N-4 chips:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
For GW1NSR-4C and GW1NR-9 chips, SP/SPX9 primitives with data widths
of 32/36 bits are implemented using a pair of 16-bit wide
primitives.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-26 11:51:16 +01:00
flags : int
2025-09-23 12:42:33 +02:00
dcs_prefix : IdString = field ( default = None )
bottom_io : BottomIO = field ( default = None )
2023-08-06 12:56:08 +02:00
diff_io_types : list [ IdString ] = field ( default_factory = list )
2024-07-14 08:53:26 +02:00
dqce_bels : list [ SpineBel ] = field ( default_factory = list )
dcs_bels : list [ SpineBel ] = field ( default_factory = list )
2024-09-11 11:18:26 +02:00
dhcen_bels : list [ WireBel ] = field ( default_factory = list )
2025-03-19 08:41:35 +01:00
io_dlldly_bels : list [ IoBel ] = field ( default_factory = list )
Gowin. Add a router for segments. (#1456)
Gowin chips have an interesting mechanism - wires that run vertically
through several rows (at least 10) in each column of the chip. In each
row a particular wire has branches to the left and right, covering on
average 4 neighboring cells in the row. For lack of a better term, I
further call such a wire a segment.
So a segment can provide a direct connection in a local rectangle. There
are no special restrictions on the sinks, so segment networks can be
used for ClockEnable, LocalSetReset, as well as for LUT and DFF inputs.
The sources are not so simple - the sources can be the upper or lower
end of the segment, which in theory can lead to unfortunate consequences
if the signal is applied from both ends.
The matter is complicated by the fact that there are default
connections, i.e. in the absence of any set fuse the segment input is
still connected to something (VCC for example) and to disable the unused
end of the segment you need to set a special combination of fuses.
Taking into account which end of which segment is used is one of the
tasks of this router. In addition, segment ends can physically coincide
with PLL, DSP and BSRAM inputs, which can also lead to unexpected
effects. Some of these things are tracked when generating the base, some
in this router, some when packing in gowin_pack.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-03-18 12:02:49 +01:00
segments : list [ Segment ] = field ( default_factory = list )
2023-07-23 08:46:04 +02:00
2025-09-23 12:42:33 +02:00
def set_dcs_prefix ( self , prefix : str ) :
self . dcs_prefix = self . strs . id ( prefix )
2023-07-23 08:46:04 +02:00
def create_bottom_io ( self ) :
self . bottom_io = BottomIO ( )
def add_bottom_io_cnd ( self , net_a : str , net_b : str ) :
self . bottom_io . conditions . append ( BottomIOCnd ( self . strs . id ( net_a ) , self . strs . id ( net_b ) ) )
2023-08-06 12:56:08 +02:00
def add_diff_io_type ( self , diff_type : str ) :
self . diff_io_types . append ( self . strs . id ( diff_type ) )
2024-09-11 11:18:26 +02:00
def add_dhcen_bel ( self , pip_xy : str , pip_dst : str , pip_src , x : int , y : int , z : int , side : str ) :
self . dhcen_bels . append ( WireBel ( self . strs . id ( pip_xy ) , self . strs . id ( pip_dst ) , self . strs . id ( pip_src ) , x , y , z , self . strs . id ( side ) ) )
2024-07-14 08:53:26 +02:00
def add_dqce_bel ( self , spine : str , x : int , y : int , z : int ) :
self . dqce_bels . append ( SpineBel ( self . strs . id ( spine ) , x , y , z ) )
def add_dcs_bel ( self , spine : str , x : int , y : int , z : int ) :
self . dcs_bels . append ( SpineBel ( self . strs . id ( spine ) , x , y , z ) )
2025-03-19 08:41:35 +01:00
def add_io_dlldly_bel ( self , io : str , dlldly : str ) :
self . io_dlldly_bels . append ( IoBel ( self . strs . id ( io ) , self . strs . id ( dlldly ) ) )
Gowin. Add a router for segments. (#1456)
Gowin chips have an interesting mechanism - wires that run vertically
through several rows (at least 10) in each column of the chip. In each
row a particular wire has branches to the left and right, covering on
average 4 neighboring cells in the row. For lack of a better term, I
further call such a wire a segment.
So a segment can provide a direct connection in a local rectangle. There
are no special restrictions on the sinks, so segment networks can be
used for ClockEnable, LocalSetReset, as well as for LUT and DFF inputs.
The sources are not so simple - the sources can be the upper or lower
end of the segment, which in theory can lead to unfortunate consequences
if the signal is applied from both ends.
The matter is complicated by the fact that there are default
connections, i.e. in the absence of any set fuse the segment input is
still connected to something (VCC for example) and to disable the unused
end of the segment you need to set a special combination of fuses.
Taking into account which end of which segment is used is one of the
tasks of this router. In addition, segment ends can physically coincide
with PLL, DSP and BSRAM inputs, which can also lead to unexpected
effects. Some of these things are tracked when generating the base, some
in this router, some when packing in gowin_pack.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-03-18 12:02:49 +01:00
def add_segment ( self , x : int , seg_idx : int , min_x : int , min_y : int , max_x : int , max_y : int ,
top_row : int , bottom_row : int , top_wire : str , bottom_wire : str , top_gate_wire : list , bottom_gate_wire : list ) :
new_seg = Segment ( x , seg_idx , min_x , min_y , max_x , max_y , top_row , bottom_row ,
self . strs . id ( top_wire ) , self . strs . id ( bottom_wire ) ,
[ self . strs . id ( top_gate_wire [ 0 ] ) ] , [ self . strs . id ( bottom_gate_wire [ 0 ] ) ] )
if top_gate_wire [ 1 ] :
new_seg . top_gate_wire . append ( self . strs . id ( top_gate_wire [ 1 ] ) )
else :
new_seg . top_gate_wire . append ( self . strs . id ( ' ' ) )
if bottom_gate_wire [ 1 ] :
new_seg . bottom_gate_wire . append ( self . strs . id ( bottom_gate_wire [ 1 ] ) )
else :
new_seg . bottom_gate_wire . append ( self . strs . id ( ' ' ) )
self . segments . append ( new_seg )
2023-07-23 08:46:04 +02:00
def serialise_lists ( self , context : str , bba : BBAWriter ) :
self . bottom_io . serialise_lists ( f " { context } _bottom_io " , bba )
Gowin. Add a router for segments. (#1456)
Gowin chips have an interesting mechanism - wires that run vertically
through several rows (at least 10) in each column of the chip. In each
row a particular wire has branches to the left and right, covering on
average 4 neighboring cells in the row. For lack of a better term, I
further call such a wire a segment.
So a segment can provide a direct connection in a local rectangle. There
are no special restrictions on the sinks, so segment networks can be
used for ClockEnable, LocalSetReset, as well as for LUT and DFF inputs.
The sources are not so simple - the sources can be the upper or lower
end of the segment, which in theory can lead to unfortunate consequences
if the signal is applied from both ends.
The matter is complicated by the fact that there are default
connections, i.e. in the absence of any set fuse the segment input is
still connected to something (VCC for example) and to disable the unused
end of the segment you need to set a special combination of fuses.
Taking into account which end of which segment is used is one of the
tasks of this router. In addition, segment ends can physically coincide
with PLL, DSP and BSRAM inputs, which can also lead to unexpected
effects. Some of these things are tracked when generating the base, some
in this router, some when packing in gowin_pack.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-03-18 12:02:49 +01:00
for i , t in enumerate ( self . segments ) :
t . serialise_lists ( f " { context } _segment { i } " , bba )
2023-08-06 12:56:08 +02:00
bba . label ( f " { context } _diff_io_types " )
for i , diff_io_type in enumerate ( self . diff_io_types ) :
bba . u32 ( diff_io_type . index )
2024-07-14 08:53:26 +02:00
bba . label ( f " { context } _dqce_bels " )
for i , t in enumerate ( self . dqce_bels ) :
t . serialise ( f " { context } _dqce_bel { i } " , bba )
bba . label ( f " { context } _dcs_bels " )
for i , t in enumerate ( self . dcs_bels ) :
t . serialise ( f " { context } _dcs_bel { i } " , bba )
2024-09-11 11:18:26 +02:00
bba . label ( f " { context } _dhcen_bels " )
for i , t in enumerate ( self . dhcen_bels ) :
t . serialise ( f " { context } _dhcen_bel { i } " , bba )
2025-03-19 08:41:35 +01:00
bba . label ( f " { context } _io_dlldly_bels " )
for i , t in enumerate ( self . io_dlldly_bels ) :
t . serialise ( f " { context } _io_dlldly_bel { i } " , bba )
Gowin. Add a router for segments. (#1456)
Gowin chips have an interesting mechanism - wires that run vertically
through several rows (at least 10) in each column of the chip. In each
row a particular wire has branches to the left and right, covering on
average 4 neighboring cells in the row. For lack of a better term, I
further call such a wire a segment.
So a segment can provide a direct connection in a local rectangle. There
are no special restrictions on the sinks, so segment networks can be
used for ClockEnable, LocalSetReset, as well as for LUT and DFF inputs.
The sources are not so simple - the sources can be the upper or lower
end of the segment, which in theory can lead to unfortunate consequences
if the signal is applied from both ends.
The matter is complicated by the fact that there are default
connections, i.e. in the absence of any set fuse the segment input is
still connected to something (VCC for example) and to disable the unused
end of the segment you need to set a special combination of fuses.
Taking into account which end of which segment is used is one of the
tasks of this router. In addition, segment ends can physically coincide
with PLL, DSP and BSRAM inputs, which can also lead to unexpected
effects. Some of these things are tracked when generating the base, some
in this router, some when packing in gowin_pack.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-03-18 12:02:49 +01:00
bba . label ( f " { context } _segments " )
for i , t in enumerate ( self . segments ) :
t . serialise ( f " { context } _segment { i } " , bba )
2023-08-06 12:56:08 +02:00
2023-07-23 08:46:04 +02:00
def serialise ( self , context : str , bba : BBAWriter ) :
gowin: Himbaechel. Add BSRAM for all chips.
The following primitives are implemented for the GW1N-1, GW2A-18,
GW2AR-18C, GW1NSR-4C, GW1NR-9C, GW1NR-9 and GW1N-4 chips:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
For GW1NSR-4C and GW1NR-9 chips, SP/SPX9 primitives with data widths
of 32/36 bits are implemented using a pair of 16-bit wide
primitives.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-26 11:51:16 +01:00
bba . u32 ( self . flags )
2025-09-23 12:42:33 +02:00
bba . u32 ( self . dcs_prefix . index )
2023-07-23 08:46:04 +02:00
self . bottom_io . serialise ( f " { context } _bottom_io " , bba )
2023-08-06 12:56:08 +02:00
bba . slice ( f " { context } _diff_io_types " , len ( self . diff_io_types ) )
2024-07-14 08:53:26 +02:00
bba . slice ( f " { context } _dqce_bels " , len ( self . dqce_bels ) )
bba . slice ( f " { context } _dcs_bels " , len ( self . dcs_bels ) )
2024-09-11 11:18:26 +02:00
bba . slice ( f " { context } _dhcen_bels " , len ( self . dhcen_bels ) )
2025-03-19 08:41:35 +01:00
bba . slice ( f " { context } _io_dlldly_bels " , len ( self . io_dlldly_bels ) )
Gowin. Add a router for segments. (#1456)
Gowin chips have an interesting mechanism - wires that run vertically
through several rows (at least 10) in each column of the chip. In each
row a particular wire has branches to the left and right, covering on
average 4 neighboring cells in the row. For lack of a better term, I
further call such a wire a segment.
So a segment can provide a direct connection in a local rectangle. There
are no special restrictions on the sinks, so segment networks can be
used for ClockEnable, LocalSetReset, as well as for LUT and DFF inputs.
The sources are not so simple - the sources can be the upper or lower
end of the segment, which in theory can lead to unfortunate consequences
if the signal is applied from both ends.
The matter is complicated by the fact that there are default
connections, i.e. in the absence of any set fuse the segment input is
still connected to something (VCC for example) and to disable the unused
end of the segment you need to set a special combination of fuses.
Taking into account which end of which segment is used is one of the
tasks of this router. In addition, segment ends can physically coincide
with PLL, DSP and BSRAM inputs, which can also lead to unexpected
effects. Some of these things are tracked when generating the base, some
in this router, some when packing in gowin_pack.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-03-18 12:02:49 +01:00
bba . slice ( f " { context } _segments " , len ( self . segments ) )
2023-07-23 08:46:04 +02:00
2024-10-09 15:16:36 +02:00
@dataclass
class PackageExtraData ( BBAStruct ) :
strs : StringPool
cst : list
def serialise_lists ( self , context : str , bba : BBAWriter ) :
bba . label ( f " { context } _constraints " )
for ( net , row , col , bel , iostd ) in self . cst :
bba . u32 ( self . strs . id ( net ) . index )
bba . u32 ( row )
bba . u32 ( col )
bba . u32 ( ord ( bel [ 0 ] ) - ord ( ' A ' ) + IOBA_Z )
bba . u32 ( self . strs . id ( iostd ) . index if iostd else 0 )
def serialise ( self , context : str , bba : BBAWriter ) :
bba . slice ( f " { context } _constraints " , len ( self . cst ) )
2024-04-07 13:47:23 +02:00
@dataclass
class PadExtraData ( BBAStruct ) :
# Which PLL does this pad belong to.
pll_tile : IdString
pll_bel : IdString
pll_type : IdString
def serialise_lists ( self , context : str , bba : BBAWriter ) :
pass
def serialise ( self , context : str , bba : BBAWriter ) :
bba . u32 ( self . pll_tile . index )
bba . u32 ( self . pll_bel . index )
bba . u32 ( self . pll_type . index )
2023-08-12 07:12:19 +02:00
# Unique features of the tiletype
class TypeDesc :
def __init__ ( self , dups , tiletype = ' ' , extra_func = None , sfx = 0 ) :
self . tiletype = tiletype
self . extra_func = extra_func
self . dups = dups
self . sfx = sfx
2023-08-06 12:56:08 +02:00
created_tiletypes = { }
2023-06-30 09:18:14 +02:00
2023-10-05 08:18:10 +02:00
# get timing class by wire name
def get_tm_class ( db : chipdb , wire : str ) :
assert wire in db . wire_delay , f " Unknown timing class for { wire } "
return db . wire_delay [ wire ]
2023-06-30 09:18:14 +02:00
# u-turn at the rim
2023-06-28 08:32:53 +02:00
uturnlut = { ' N ' : ' S ' , ' S ' : ' N ' , ' E ' : ' W ' , ' W ' : ' E ' }
def uturn ( db : chipdb , x : int , y : int , wire : str ) :
m = re . match ( r " ([NESW])([128] \ d)( \ d) " , wire )
if m :
direction , num , segment = m . groups ( )
# wires wrap around the edges
# assumes 0-based indexes
if y < 0 :
y = - 1 - y
direction = uturnlut [ direction ]
if x < 0 :
x = - 1 - x
direction = uturnlut [ direction ]
if y > db . rows - 1 :
y = 2 * db . rows - 1 - y
direction = uturnlut [ direction ]
if x > db . cols - 1 :
x = 2 * db . cols - 1 - x
direction = uturnlut [ direction ]
wire = f ' { direction } { num } { segment } '
return ( x , y , wire )
2023-06-28 02:16:29 +02:00
def create_nodes ( chip : Chip , db : chipdb ) :
2023-06-28 08:32:53 +02:00
# : (x, y)
dirs = { ' N ' : ( 0 , - 1 ) , ' S ' : ( 0 , 1 ) , ' W ' : ( - 1 , 0 ) , ' E ' : ( 1 , 0 ) }
X = db . cols
Y = db . rows
2023-06-30 09:18:14 +02:00
global_nodes = { }
2023-06-28 08:32:53 +02:00
for y in range ( Y ) :
for x in range ( X ) :
nodes = [ ]
2023-07-12 03:25:28 +02:00
tt = chip . tile_type_at ( x , y )
extra_tile_data = tt . extra_data
2023-06-28 08:32:53 +02:00
# SN and EW
for i in [ 1 , 2 ] :
nodes . append ( [ NodeWire ( x , y , f ' SN { i } 0 ' ) ,
NodeWire ( * uturn ( db , x , y - 1 , f ' N1 { i } 1 ' ) ) ,
NodeWire ( * uturn ( db , x , y + 1 , f ' S1 { i } 1 ' ) ) ] )
nodes . append ( [ NodeWire ( x , y , f ' EW { i } 0 ' ) ,
NodeWire ( * uturn ( db , x - 1 , y , f ' W1 { i } 1 ' ) ) ,
NodeWire ( * uturn ( db , x + 1 , y , f ' E1 { i } 1 ' ) ) ] )
for d , offs in dirs . items ( ) :
# 1-hop
for i in [ 0 , 3 ] :
nodes . append ( [ NodeWire ( x , y , f ' { d } 1 { i } 0 ' ) ,
NodeWire ( * uturn ( db , x + offs [ 0 ] , y + offs [ 1 ] , f ' { d } 1 { i } 1 ' ) ) ] )
# 2-hop
for i in range ( 8 ) :
nodes . append ( [ NodeWire ( x , y , f ' { d } 2 { i } 0 ' ) ,
NodeWire ( * uturn ( db , x + offs [ 0 ] , y + offs [ 1 ] , f ' { d } 2 { i } 1 ' ) ) ,
NodeWire ( * uturn ( db , x + offs [ 0 ] * 2 , y + offs [ 1 ] * 2 , f ' { d } 2 { i } 2 ' ) ) ] )
# 4-hop
for i in range ( 4 ) :
nodes . append ( [ NodeWire ( x , y , f ' { d } 8 { i } 0 ' ) ,
NodeWire ( * uturn ( db , x + offs [ 0 ] * 4 , y + offs [ 1 ] * 4 , f ' { d } 8 { i } 4 ' ) ) ,
NodeWire ( * uturn ( db , x + offs [ 0 ] * 8 , y + offs [ 1 ] * 8 , f ' { d } 8 { i } 8 ' ) ) ] )
2023-07-02 08:09:39 +02:00
# I0 for MUX2_LUT8
2023-07-05 04:49:25 +02:00
if ( x < X - 1 and extra_tile_data . tile_class == chip . strs . id ( ' LOGIC ' )
and chip . tile_type_at ( x + 1 , y ) . extra_data . tile_class == chip . strs . id ( ' LOGIC ' ) ) :
2023-07-02 08:09:39 +02:00
nodes . append ( [ NodeWire ( x , y , ' OF30 ' ) ,
2023-07-05 04:49:25 +02:00
NodeWire ( x + 1 , y , ' OF3 ' ) ] )
# ALU
if extra_tile_data . tile_class == chip . strs . id ( ' LOGIC ' ) :
# local carry chain
for i in range ( 5 ) :
nodes . append ( [ NodeWire ( x , y , f ' COUT { i } ' ) ,
NodeWire ( x , y , f ' CIN { i + 1 } ' ) ] ) ;
2025-11-06 09:17:05 +01:00
# global carry chain
2023-07-05 04:49:25 +02:00
if x > 1 and chip . tile_type_at ( x - 1 , y ) . extra_data . tile_class == chip . strs . id ( ' LOGIC ' ) :
nodes . append ( [ NodeWire ( x , y , f ' CIN0 ' ) ,
NodeWire ( x - 1 , y , f ' COUT5 ' ) ] )
2023-06-28 08:32:53 +02:00
for node in nodes :
chip . add_node ( node )
2023-07-05 04:49:25 +02:00
2023-06-30 09:18:14 +02:00
# VCC and VSS sources in the all tiles
global_nodes . setdefault ( ' GND ' , [ ] ) . append ( NodeWire ( x , y , ' VSS ' ) )
global_nodes . setdefault ( ' VCC ' , [ ] ) . append ( NodeWire ( x , y , ' VCC ' ) )
2023-07-12 03:25:28 +02:00
# add nodes from the apicula db
2023-07-22 02:01:35 +02:00
for node_name , node_hdr in db . nodes . items ( ) :
wire_type , node = node_hdr
2023-08-06 12:56:08 +02:00
if len ( node ) < 2 :
continue
2025-03-19 08:41:35 +01:00
min_wire_name_len = 0
if node :
min_wire_name_len = len ( next ( iter ( node ) ) [ 2 ] )
2023-07-12 03:25:28 +02:00
for y , x , wire in node :
2023-07-22 02:01:35 +02:00
if wire_type :
2023-07-25 05:25:33 +02:00
if not chip . tile_type_at ( x , y ) . has_wire ( wire ) :
chip . tile_type_at ( x , y ) . create_wire ( wire , wire_type )
else :
chip . tile_type_at ( x , y ) . set_wire_type ( wire , wire_type )
2023-07-12 03:25:28 +02:00
new_node = NodeWire ( x , y , wire )
gl_nodes = global_nodes . setdefault ( node_name , [ ] )
if new_node not in gl_nodes :
2025-03-19 08:41:35 +01:00
if len ( wire ) < min_wire_name_len :
min_wire_name_len = len ( wire )
gl_nodes . insert ( 0 , new_node )
else :
gl_nodes . append ( new_node )
2023-07-12 03:25:28 +02:00
for name , node in global_nodes . items ( ) :
2023-06-30 09:18:14 +02:00
chip . add_node ( node )
2023-06-28 02:16:29 +02:00
def create_switch_matrix ( tt : TileType , db : chipdb , x : int , y : int ) :
2023-07-12 03:25:28 +02:00
def get_wire_type ( name ) :
2023-07-22 02:01:35 +02:00
if name in { ' XD0 ' , ' XD1 ' , ' XD2 ' , ' XD3 ' , ' XD4 ' , ' XD5 ' , } :
2023-07-19 05:29:18 +02:00
return " X0 "
2025-03-19 08:41:35 +01:00
if name in { " PCLK_DUMMY " } :
return " GLOBAL_CLK "
if name in { " DLLDLY_OUT " } :
return " DLLDLY_O "
Gowin. Add a router for segments. (#1456)
Gowin chips have an interesting mechanism - wires that run vertically
through several rows (at least 10) in each column of the chip. In each
row a particular wire has branches to the left and right, covering on
average 4 neighboring cells in the row. For lack of a better term, I
further call such a wire a segment.
So a segment can provide a direct connection in a local rectangle. There
are no special restrictions on the sinks, so segment networks can be
used for ClockEnable, LocalSetReset, as well as for LUT and DFF inputs.
The sources are not so simple - the sources can be the upper or lower
end of the segment, which in theory can lead to unfortunate consequences
if the signal is applied from both ends.
The matter is complicated by the fact that there are default
connections, i.e. in the absence of any set fuse the segment input is
still connected to something (VCC for example) and to disable the unused
end of the segment you need to set a special combination of fuses.
Taking into account which end of which segment is used is one of the
tasks of this router. In addition, segment ends can physically coincide
with PLL, DSP and BSRAM inputs, which can also lead to unexpected
effects. Some of these things are tracked when generating the base, some
in this router, some when packing in gowin_pack.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-03-18 12:02:49 +01:00
if name in { ' LT00 ' , ' LT10 ' , ' LT20 ' , ' LT30 ' , ' LT02 ' , ' LT13 ' } :
return " LW_TAP "
2023-07-12 03:25:28 +02:00
return " "
for dst , srcs in db . grid [ y ] [ x ] . pips . items ( ) :
if not tt . has_wire ( dst ) :
tt . create_wire ( dst , get_wire_type ( dst ) )
for src in srcs . keys ( ) :
2025-09-20 07:51:01 +02:00
assert src in db . wire_delay , f " No timing info for { src } wire "
2023-07-12 03:25:28 +02:00
if not tt . has_wire ( src ) :
2024-07-14 08:53:26 +02:00
if src in { " VSS " , " VCC " } :
tt . create_wire ( src , get_wire_type ( src ) , const_value = src )
else :
tt . create_wire ( src , get_wire_type ( src ) )
2023-10-05 08:18:10 +02:00
tt . create_pip ( src , dst , get_tm_class ( db , src ) )
2023-08-06 12:56:08 +02:00
2023-07-12 03:25:28 +02:00
# clock wires
2025-07-23 08:45:24 +02:00
for dst , srcs in db . grid [ y ] [ x ] . clock_pips . items ( ) :
2023-06-28 02:16:29 +02:00
if not tt . has_wire ( dst ) :
2023-07-12 03:25:28 +02:00
tt . create_wire ( dst , " GLOBAL_CLK " )
2023-06-28 02:16:29 +02:00
for src in srcs . keys ( ) :
if not tt . has_wire ( src ) :
2023-07-12 03:25:28 +02:00
tt . create_wire ( src , " GLOBAL_CLK " )
2024-08-17 23:43:07 +02:00
src_tm_class = get_tm_class ( db , src )
2024-09-05 22:39:26 +02:00
tt . create_pip ( src , dst , src_tm_class )
2023-06-28 02:16:29 +02:00
2023-08-06 12:56:08 +02:00
def create_hclk_switch_matrix ( tt : TileType , db : chipdb , x : int , y : int ) :
2023-08-12 07:12:19 +02:00
if ( y , x ) not in db . hclk_pips :
return
2023-08-06 12:56:08 +02:00
# hclk wires
2023-08-12 07:12:19 +02:00
for dst , srcs in db . hclk_pips [ y , x ] . items ( ) :
2023-08-06 12:56:08 +02:00
if not tt . has_wire ( dst ) :
tt . create_wire ( dst , " HCLK " )
for src in srcs . keys ( ) :
if not tt . has_wire ( src ) :
tt . create_wire ( src , " HCLK " )
2023-10-05 08:18:10 +02:00
tt . create_pip ( src , dst , get_tm_class ( db , " X01 " ) ) # XXX
2024-08-21 12:22:07 +02:00
2024-08-03 15:57:22 +02:00
hclk_bel_zs = {
" CLKDIV2_HCLK0_SECT0 " : CLKDIV2_0_Z ,
" CLKDIV2_HCLK0_SECT1 " : CLKDIV2_1_Z ,
" CLKDIV2_HCLK1_SECT0 " : CLKDIV2_2_Z ,
" CLKDIV2_HCLK1_SECT1 " : CLKDIV2_3_Z ,
" CLKDIV_HCLK0_SECT0 " : CLKDIV_0_Z ,
" CLKDIV_HCLK0_SECT1 " : CLKDIV_1_Z ,
" CLKDIV_HCLK1_SECT0 " : CLKDIV_2_Z ,
" CLKDIV_HCLK1_SECT1 " : CLKDIV_3_Z
}
2024-08-21 12:22:07 +02:00
2024-08-03 15:57:22 +02:00
for bel_name , bel_props in db . grid [ y ] [ x ] . bels . items ( ) :
if ( bel_name not in hclk_bel_zs ) :
continue
this_portmap = bel_props . portmap
if bel_name . startswith ( " CLKDIV2_ " ) :
bel_type = " CLKDIV2 "
elif bel_name . startswith ( " CLKDIV_ " ) :
bel_type = " CLKDIV "
this_bel = tt . create_bel ( bel_name , bel_type , hclk_bel_zs [ bel_name ] )
2024-08-21 12:22:07 +02:00
if ( bel_name in [ " CLKDIV_HCLK0_SECT1 " , " CLKDIV_HCLK1_SECT1 " ] ) :
2024-08-03 15:57:22 +02:00
this_bel . flags | = BEL_FLAG_HIDDEN
if bel_type == " CLKDIV " :
this_bel . flags | = BEL_FLAG_GLOBAL
known_pins = [ " HCLKIN " , " RESETN " , " CLKOUT " ]
if bel_type == " CLKDIV " :
known_pins . append ( " CALIB " )
for pin in this_portmap . keys ( ) :
assert pin in known_pins , f " Unknown pin { pin } for bel { this_bel } "
if pin in [ " CALIB " , " RESETN " , " HCLKIN " ] :
pin_direction = PinType . INPUT
elif pin in [ " CLKOUT " ] :
pin_direction = PinType . OUTPUT
wire_type = " HCLK_CTRL " if pin in ( " CALIB " , " RESETN " ) else " HCLK "
add_port_wire ( tt , this_bel , this_portmap , pin , wire_type , pin_direction )
2023-08-06 12:56:08 +02:00
2024-07-14 08:53:26 +02:00
# map spine -> dqce bel
dqce_bels = { }
# map spine -> dcs bel
dcs_bels = { }
2024-09-11 11:18:26 +02:00
# map HCLKIN wire -> dhcen bel
dhcen_bels = { }
2025-03-19 08:41:35 +01:00
# map io bel -> dlldly bel
io_dlldly_bels = { }
2023-08-12 07:12:19 +02:00
def create_extra_funcs ( tt : TileType , db : chipdb , x : int , y : int ) :
if ( y , x ) not in db . extra_func :
return
for func , desc in db . extra_func [ ( y , x ) ] . items ( ) :
if func == ' osc ' :
osc_type = desc [ ' type ' ]
portmap = db . grid [ y ] [ x ] . bels [ osc_type ] . portmap
for port , wire in portmap . items ( ) :
2024-07-14 08:53:26 +02:00
if not tt . has_wire ( wire ) :
tt . create_wire ( wire , port )
2023-08-12 07:12:19 +02:00
bel = tt . create_bel ( osc_type , osc_type , z = OSC_Z )
for port , wire in portmap . items ( ) :
if ' OUT ' in port :
tt . add_bel_pin ( bel , port , wire , PinType . OUTPUT )
else :
tt . add_bel_pin ( bel , port , wire , PinType . INPUT )
elif func == ' gsr ' :
wire = desc [ ' wire ' ]
2024-07-14 08:53:26 +02:00
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
2023-08-12 07:12:19 +02:00
bel = tt . create_bel ( " GSR " , " GSR " , z = GSR_Z )
tt . add_bel_pin ( bel , " GSRI " , wire , PinType . INPUT )
2024-07-06 11:14:43 +02:00
elif func == ' bandgap ' :
wire = desc [ ' wire ' ]
2024-07-14 08:53:26 +02:00
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
2024-07-06 11:14:43 +02:00
bel = tt . create_bel ( " BANDGAP " , " BANDGAP " , z = BANDGAP_Z )
tt . add_bel_pin ( bel , " BGEN " , wire , PinType . INPUT )
2024-09-11 11:18:26 +02:00
elif func == ' dhcen ' :
for idx , dhcen in enumerate ( desc ) :
wire = dhcen [ ' ce ' ]
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
bel_z = DHCEN_Z + idx
bel = tt . create_bel ( f " DHCEN { idx } " , " DHCEN " , z = bel_z )
tt . add_bel_pin ( bel , " CE " , wire , PinType . INPUT )
pip_xy , pip_dst , pip_src , side = dhcen [ ' pip ' ]
dhcen_bels [ pip_xy , pip_dst , pip_src ] = ( x , y , bel_z , side )
2025-03-19 08:41:35 +01:00
elif func == ' dlldly ' :
for idx , dlldly in desc . items ( ) :
bel_z = DLLDLY_Z + idx
bel = tt . create_bel ( f " DLLDLY { idx } " , " DLLDLY " , z = bel_z )
for pin , wire in dlldly [ ' in_wires ' ] . items ( ) :
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
tt . add_bel_pin ( bel , pin , wire , PinType . INPUT )
for pin , wire in dlldly [ ' out_wires ' ] . items ( ) :
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
tt . add_bel_pin ( bel , pin , wire , PinType . OUTPUT )
io_dlldly_bels [ f " { dlldly [ ' io_loc ' ] } / { dlldly [ ' io_bel ' ] } " ] = f " X { x } Y { y } /DLLDLY { idx } "
2024-07-14 08:53:26 +02:00
elif func == ' dqce ' :
for idx in range ( 6 ) :
bel_z = DQCE_Z + idx
bel = tt . create_bel ( f " DQCE { idx } " , " DQCE " , bel_z )
wire = desc [ idx ] [ ' clkin ' ]
dqce_bels [ wire ] = ( x , y , bel_z )
if not tt . has_wire ( wire ) :
tt . create_wire ( wire , " GLOBAL_CLK " )
tt . add_bel_pin ( bel , " CLKIN " , wire , PinType . INPUT )
tt . add_bel_pin ( bel , " CLKOUT " , wire , PinType . OUTPUT )
wire = desc [ idx ] [ ' ce ' ]
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
tt . add_bel_pin ( bel , " CE " , wire , PinType . INPUT )
elif func == ' dcs ' :
for idx in range ( 2 ) :
if idx not in desc :
continue
2025-09-23 12:42:33 +02:00
dcs_prefix = ' CLK '
if hasattr ( db , " dcs_prefix " ) :
dcs_prefix = db . dcs_prefix
2024-07-14 08:53:26 +02:00
bel_z = DCS_Z + idx
bel = tt . create_bel ( f " DCS { idx } " , " DCS " , bel_z )
wire = desc [ idx ] [ ' clkout ' ]
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
tt . add_bel_pin ( bel , " CLKOUT " , wire , PinType . OUTPUT )
clkout_wire = wire
for clk_idx , wire in enumerate ( desc [ idx ] [ ' clk ' ] ) :
if not tt . has_wire ( wire ) :
tt . create_wire ( wire , " GLOBAL_CLK " )
2025-09-23 12:42:33 +02:00
tt . add_bel_pin ( bel , f " { dcs_prefix } { clk_idx } " , wire , PinType . INPUT )
2024-07-14 08:53:26 +02:00
# This is a fake PIP that allows routing “through” this
# primitive from the CLK input to the CLKOUT output.
tt . create_pip ( wire , clkout_wire )
dcs_bels [ wire ] = ( x , y , bel_z )
for i , wire in enumerate ( desc [ idx ] [ ' clksel ' ] ) :
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
tt . add_bel_pin ( bel , f " CLKSEL { i } " , wire , PinType . INPUT )
wire = desc [ idx ] [ ' selforce ' ]
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
tt . add_bel_pin ( bel , " SELFORCE " , wire , PinType . INPUT )
elif func == ' io16 ' :
2023-08-13 14:05:18 +02:00
role = desc [ ' role ' ]
if role == ' MAIN ' :
y_off , x_off = desc [ ' pair ' ]
tt . extra_data . io16_x_off = x_off
tt . extra_data . io16_y_off = y_off
for io_type , z in { ( ' IDES16 ' , IDES16_Z ) , ( ' OSER16 ' , OSER16_Z ) } :
bel = tt . create_bel ( io_type , io_type , z = z )
portmap = db . grid [ y ] [ x ] . bels [ io_type ] . portmap
for port , wire in portmap . items ( ) :
if port == ' FCLK ' : # XXX compatibility
wire = ' FCLKA '
if not tt . has_wire ( wire ) :
if port in { ' CLK ' , ' PCLK ' } :
tt . create_wire ( wire , " TILE_CLK " )
else :
tt . create_wire ( wire , " IOL_PORT " )
if ' OUT ' in port :
tt . add_bel_pin ( bel , port , wire , PinType . OUTPUT )
else :
tt . add_bel_pin ( bel , port , wire , PinType . INPUT )
2025-01-29 14:02:21 +01:00
elif func == ' i3c_capable ' :
tt . extra_data . tile_flags | = TILE_I3C_CAPABLE_IO
2025-01-23 10:17:31 +01:00
elif func == ' mipi_obuf ' :
bel = tt . create_bel ( ' MIPI_OBUF ' , ' MIPI_OBUF ' , MIPIOBUF_Z )
elif func == ' mipi_ibuf ' :
bel = tt . create_bel ( ' MIPI_IBUF ' , ' MIPI_IBUF ' , MIPIIBUF_Z )
wire = desc [ ' HSREN ' ]
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
tt . add_bel_pin ( bel , ' HSREN ' , wire , PinType . INPUT )
wire = ' MIPIOL '
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
tt . add_bel_pin ( bel , ' OL ' , wire , PinType . OUTPUT )
for i in range ( 2 ) :
wire = f ' MIPIEN { i } '
if not tt . has_wire ( wire ) :
tt . create_wire ( wire )
tt . add_bel_pin ( bel , f ' MIPIEN { i } ' , wire , PinType . INPUT )
2024-07-14 08:53:26 +02:00
elif func == ' buf ' :
2023-09-04 14:20:08 +02:00
for buf_type , wires in desc . items ( ) :
for i , wire in enumerate ( wires ) :
if not tt . has_wire ( wire ) :
tt . create_wire ( wire , " TILE_CLK " )
wire_out = f ' { buf_type } { i } _O '
2024-07-14 08:53:26 +02:00
tt . create_wire ( wire_out , " BUFG_O " )
2023-09-04 14:20:08 +02:00
# XXX make Z from buf_type
bel = tt . create_bel ( f ' { buf_type } { i } ' , buf_type , z = BUFG_Z + i )
bel . flags = BEL_FLAG_GLOBAL
tt . add_bel_pin ( bel , " I " , wire , PinType . INPUT )
tt . add_bel_pin ( bel , " O " , wire_out , PinType . OUTPUT )
2024-09-04 12:55:35 +02:00
elif func == ' userflash ' :
bel = tt . create_bel ( " USERFLASH " , desc [ ' type ' ] , USERFLASH_Z )
portmap = desc [ ' ins ' ]
for port , wire in portmap . items ( ) :
if not tt . has_wire ( wire ) :
tt . create_wire ( wire , " FLASH_IN " )
tt . add_bel_pin ( bel , port , wire , PinType . INPUT )
portmap = desc [ ' outs ' ]
for port , wire in portmap . items ( ) :
if not tt . has_wire ( wire ) :
tt . create_wire ( wire , " FLASH_OUT " )
tt . add_bel_pin ( bel , port , wire , PinType . OUTPUT )
2024-09-12 09:53:39 +02:00
elif func == ' emcu ' :
bel = tt . create_bel ( " EMCU " , " EMCU " , EMCU_Z )
portmap = desc [ ' ins ' ]
for port , wire in portmap . items ( ) :
if not tt . has_wire ( wire ) :
tt . create_wire ( wire , " EMCU_IN " )
tt . add_bel_pin ( bel , port , wire , PinType . INPUT )
portmap = desc [ ' outs ' ]
for port , wire in portmap . items ( ) :
if not tt . has_wire ( wire ) :
tt . create_wire ( wire , " EMCU_OUT " )
tt . add_bel_pin ( bel , port , wire , PinType . OUTPUT )
2025-08-07 23:10:18 +02:00
elif func == ' pincfg ' :
bel = tt . create_bel ( " PINCFG " , " PINCFG " , PINCFG_Z )
portmap = desc [ ' ins ' ]
for port , wire in portmap . items ( ) :
if not tt . has_wire ( wire ) :
tt . create_wire ( wire , " PINCFG_IN " )
tt . add_bel_pin ( bel , port , wire , PinType . INPUT )
2025-09-20 07:51:01 +02:00
elif func == ' pll ' :
pll = tt . create_bel ( " PLL " , " PLLA " , z = PLL_Z )
pll . flags = BEL_FLAG_GLOBAL
for pin , wire in desc [ ' outputs ' ] . items ( ) :
tt . create_wire ( wire , " PLL_O " )
tt . add_bel_pin ( pll , pin , wire , PinType . OUTPUT )
for pin , wire in desc [ ' inputs ' ] . items ( ) :
tt . create_wire ( wire , " PLL_I " )
tt . add_bel_pin ( pll , pin , wire , PinType . INPUT )
2025-11-06 09:17:05 +01:00
elif func == ' adc ' :
pll = tt . create_bel ( " ADC " , " ADC " , z = ADC_Z )
for pin , wire in desc [ ' outputs ' ] . items ( ) :
tt . create_wire ( wire , " ADC_O " )
tt . add_bel_pin ( pll , pin , wire , PinType . OUTPUT )
for pin , wire in desc [ ' inputs ' ] . items ( ) :
if pin == ' CLK ' or pin == ' MDRP_CLK ' :
tt . create_wire ( wire , " TILE_CLK " )
else :
tt . create_wire ( wire , " ADC_I " )
tt . add_bel_pin ( pll , pin , wire , PinType . INPUT )
2025-10-04 15:02:53 +02:00
elif func == ' gnd_source ' :
# GND is the logic low level generator
tt . create_wire ( ' VSS ' , ' GND ' , const_value = ' VSS ' )
gnd = tt . create_bel ( ' GND ' , ' GND ' , z = GND_Z )
tt . add_bel_pin ( gnd , " G " , " VSS " , PinType . OUTPUT )
elif func == ' vcc_source ' :
# VCC is the logic high level generator
tt . create_wire ( ' VCC ' , ' VCC ' , const_value = ' VCC ' )
gnd = tt . create_bel ( ' VCC ' , ' VCC ' , z = VCC_Z )
tt . add_bel_pin ( gnd , " V " , " VCC " , PinType . OUTPUT )
2023-08-12 07:12:19 +02:00
2025-09-02 07:51:08 +02:00
def set_wire_flags ( tt : TileType , tdesc : TypeDesc ) :
if tdesc . extra_func and ' clock_gates ' in tdesc . extra_func :
for wire_name in tdesc . extra_func [ ' clock_gates ' ] :
wname_id = tt . strs . id ( wire_name )
for wire_data in tt . wires :
if wire_data . name == wname_id :
wire_data . flags | = WIRE_FLAG_CLOCK_GATE
2023-08-06 12:56:08 +02:00
def create_tiletype ( create_func , chip : Chip , db : chipdb , x : int , y : int , ttyp : int ) :
2023-08-12 07:12:19 +02:00
has_extra_func = ( y , x ) in db . extra_func
# (found, TypeDesc)
def find_or_make_dup ( ) :
for d in created_tiletypes [ ttyp ] . dups :
if has_extra_func and d . extra_func == db . extra_func [ ( y , x ) ] :
return ( True , d )
elif not has_extra_func and not d . extra_func :
return ( True , d )
sfx = len ( created_tiletypes [ ttyp ] . dups ) + 1
if has_extra_func :
tdesc = TypeDesc ( extra_func = db . extra_func [ ( y , x ) ] , sfx = sfx , dups = [ ] )
else :
tdesc = TypeDesc ( sfx = sfx , dups = [ ] )
created_tiletypes [ ttyp ] . dups . append ( tdesc )
return ( False , tdesc )
old_type = False
if ttyp not in created_tiletypes :
# new type
if has_extra_func :
tdesc = TypeDesc ( extra_func = db . extra_func [ ( y , x ) ] , dups = [ ] )
2023-08-06 12:56:08 +02:00
else :
2023-08-12 07:12:19 +02:00
tdesc = TypeDesc ( dups = [ ] )
created_tiletypes . update ( { ttyp : tdesc } )
2023-08-06 12:56:08 +02:00
else :
2023-08-12 07:12:19 +02:00
# find similar
if has_extra_func :
if created_tiletypes [ ttyp ] . extra_func == db . extra_func [ ( y , x ) ] :
tdesc = created_tiletypes [ ttyp ]
old_type = True
else :
old_type , tdesc = find_or_make_dup ( )
elif not created_tiletypes [ ttyp ] . extra_func :
tdesc = created_tiletypes [ ttyp ]
old_type = True
else :
old_type , tdesc = find_or_make_dup ( )
if old_type :
chip . set_tile_type ( x , y , tdesc . tiletype )
return
2023-08-06 12:56:08 +02:00
2023-08-12 07:12:19 +02:00
tt = create_func ( chip , db , x , y , ttyp , tdesc )
2023-08-06 12:56:08 +02:00
2023-08-12 07:12:19 +02:00
create_extra_funcs ( tt , db , x , y )
create_hclk_switch_matrix ( tt , db , x , y )
2023-08-06 12:56:08 +02:00
create_switch_matrix ( tt , db , x , y )
2025-09-02 07:51:08 +02:00
set_wire_flags ( tt , tdesc )
2023-08-12 07:12:19 +02:00
chip . set_tile_type ( x , y , tdesc . tiletype )
2023-08-06 12:56:08 +02:00
2024-04-17 06:52:34 +02:00
def add_port_wire ( tt , bel , portmap , name , wire_type , port_type , pin_name = None ) :
2024-03-18 13:08:52 +01:00
wire = portmap [ name ]
if not tt . has_wire ( wire ) :
if name . startswith ( ' CLK ' ) :
tt . create_wire ( wire , " TILE_CLK " )
else :
tt . create_wire ( wire , wire_type )
2024-04-17 06:52:34 +02:00
if pin_name :
tt . add_bel_pin ( bel , pin_name , wire , port_type )
else :
tt . add_bel_pin ( bel , name , wire , port_type )
2024-03-18 13:08:52 +01:00
2023-08-12 07:12:19 +02:00
def create_null_tiletype ( chip : Chip , db : chipdb , x : int , y : int , ttyp : int , tdesc : TypeDesc ) :
2023-07-02 08:09:39 +02:00
typename = " NULL "
2023-08-06 12:56:08 +02:00
tiletype = f " { typename } _ { ttyp } "
2023-08-12 07:12:19 +02:00
if tdesc . sfx != 0 :
tiletype + = f " _ { tdesc . sfx } "
2023-08-06 12:56:08 +02:00
tt = chip . create_tile_type ( tiletype )
2023-07-02 08:09:39 +02:00
tt . extra_data = TileExtraData ( chip . strs . id ( typename ) )
2023-08-12 07:12:19 +02:00
tdesc . tiletype = tiletype
return tt
2023-06-30 09:18:14 +02:00
2023-08-06 12:56:08 +02:00
# IO
2023-08-12 07:12:19 +02:00
def create_io_tiletype ( chip : Chip , db : chipdb , x : int , y : int , ttyp : int , tdesc : TypeDesc ) :
2023-07-02 08:09:39 +02:00
typename = " IO "
2023-08-06 12:56:08 +02:00
tiletype = f " { typename } _ { ttyp } "
2023-08-12 07:12:19 +02:00
if tdesc . sfx != 0 :
tiletype + = f " _ { tdesc . sfx } "
2023-08-06 12:56:08 +02:00
tt = chip . create_tile_type ( tiletype )
2023-07-02 08:09:39 +02:00
tt . extra_data = TileExtraData ( chip . strs . id ( typename ) )
2023-08-15 05:14:29 +02:00
simple_io = y in db . simplio_rows and chip . name in { ' GW1N-1 ' , ' GW1NZ-1 ' , ' GW1N-4 ' }
2023-07-25 05:25:33 +02:00
if simple_io :
rng = 10
else :
rng = 2
for i in range ( rng ) :
name = ' IOB ' + ' ABCDEFGHIJ ' [ i ]
# XXX some IOBs excluded from generic chipdb for some reason
2023-08-06 12:56:08 +02:00
if name not in db . grid [ y ] [ x ] . bels :
2023-07-25 05:25:33 +02:00
continue
2023-06-28 08:32:53 +02:00
# wires
portmap = db . grid [ y ] [ x ] . bels [ name ] . portmap
tt . create_wire ( portmap [ ' I ' ] , " IO_I " )
2023-07-12 03:25:28 +02:00
tt . create_wire ( portmap [ ' O ' ] , " IO_O " )
2023-07-25 05:25:33 +02:00
tt . create_wire ( portmap [ ' OE ' ] , " IO_OE " )
2023-06-28 08:32:53 +02:00
# bels
2023-07-23 08:46:04 +02:00
io = tt . create_bel ( name , " IOB " , z = IOBA_Z + i )
2023-08-19 09:49:25 +02:00
if simple_io and chip . name in { ' GW1N-1 ' } :
2023-07-25 05:25:33 +02:00
io . flags | = BEL_FLAG_SIMPLE_IO
2023-06-28 08:32:53 +02:00
tt . add_bel_pin ( io , " I " , portmap [ ' I ' ] , PinType . INPUT )
2023-08-06 12:56:08 +02:00
tt . add_bel_pin ( io , " OEN " , portmap [ ' OE ' ] , PinType . INPUT )
2023-06-28 08:32:53 +02:00
tt . add_bel_pin ( io , " O " , portmap [ ' O ' ] , PinType . OUTPUT )
2023-07-23 08:46:04 +02:00
# bottom io
2025-01-12 08:12:06 +01:00
if ' BOTTOM_IO_PORT_A ' in portmap and portmap [ ' BOTTOM_IO_PORT_A ' ] :
2023-07-23 08:46:04 +02:00
if not tt . has_wire ( portmap [ ' BOTTOM_IO_PORT_A ' ] ) :
tt . create_wire ( portmap [ ' BOTTOM_IO_PORT_A ' ] , " IO_I " )
tt . create_wire ( portmap [ ' BOTTOM_IO_PORT_B ' ] , " IO_I " )
tt . add_bel_pin ( io , " BOTTOM_IO_PORT_A " , portmap [ ' BOTTOM_IO_PORT_A ' ] , PinType . INPUT )
tt . add_bel_pin ( io , " BOTTOM_IO_PORT_B " , portmap [ ' BOTTOM_IO_PORT_B ' ] , PinType . INPUT )
2023-08-06 12:56:08 +02:00
# create IOLOGIC bels if any
for idx , name in { ( IOLOGICA_Z , ' IOLOGICA ' ) , ( IOLOGICA_Z + 1 , ' IOLOGICB ' ) } :
if name not in db . grid [ y ] [ x ] . bels :
continue
2024-02-09 08:44:57 +01:00
for off , io_type in { ( 0 , ' O ' ) , ( 2 , ' I ' ) } :
iol = tt . create_bel ( f " { name } { io_type } " , f " IOLOGIC { io_type } " , z = idx + off )
for port , wire in db . grid [ y ] [ x ] . bels [ name ] . portmap . items ( ) :
if port == ' FCLK ' : # XXX compatibility
wire = f ' FCLK { name [ - 1 ] } '
if not tt . has_wire ( wire ) :
2024-11-27 09:57:34 +01:00
if port in { ' CLK ' , ' PCLK ' , ' MCLK ' } :
2024-02-09 08:44:57 +01:00
tt . create_wire ( wire , " TILE_CLK " )
else :
tt . create_wire ( wire , " IOL_PORT " )
if port in { ' Q ' , ' Q0 ' , ' Q1 ' , ' Q2 ' , ' Q3 ' , ' Q4 ' , ' Q5 ' , ' Q6 ' , ' Q7 ' , ' Q8 ' , ' Q9 ' , ' DF ' , ' LAG ' , ' LEAD ' } :
tt . add_bel_pin ( iol , port , wire , PinType . OUTPUT )
2023-08-06 12:56:08 +02:00
else :
2024-02-09 08:44:57 +01:00
tt . add_bel_pin ( iol , port , wire , PinType . INPUT )
2023-08-12 07:12:19 +02:00
tdesc . tiletype = tiletype
return tt
2023-06-28 02:16:29 +02:00
2023-07-05 04:49:25 +02:00
# logic: luts, dffs, alu etc
2023-08-12 07:12:19 +02:00
def create_logic_tiletype ( chip : Chip , db : chipdb , x : int , y : int , ttyp : int , tdesc : TypeDesc ) :
2023-07-02 08:09:39 +02:00
typename = " LOGIC "
2023-08-06 12:56:08 +02:00
tiletype = f " { typename } _ { ttyp } "
2023-08-12 07:12:19 +02:00
if tdesc . sfx != 0 :
tiletype + = f " _ { tdesc . sfx } "
2023-08-06 12:56:08 +02:00
tt = chip . create_tile_type ( tiletype )
2023-07-02 08:09:39 +02:00
tt . extra_data = TileExtraData ( chip . strs . id ( typename ) )
2023-07-05 04:49:25 +02:00
lut_inputs = [ ' A ' , ' B ' , ' C ' , ' D ' ]
# setup LUT wires
2023-07-02 08:09:39 +02:00
for i in range ( 8 ) :
2023-06-28 02:16:29 +02:00
for inp_name in lut_inputs :
2023-10-05 08:18:10 +02:00
tt . create_wire ( f " { inp_name } { i } " , " LUT_IN " )
2023-06-28 02:16:29 +02:00
tt . create_wire ( f " F { i } " , " LUT_OUT " )
# experimental. the wire is false - it is assumed that DFF is always
# connected to the LUT's output F{i}, but we can place primitives
# arbitrarily and create a pass-through LUT afterwards.
# just out of curiosity
tt . create_wire ( f " XD { i } " , " FF_INPUT " )
tt . create_wire ( f " Q { i } " , " FF_OUT " )
2023-07-05 04:49:25 +02:00
# setup DFF wires
2023-06-28 02:16:29 +02:00
for j in range ( 3 ) :
tt . create_wire ( f " CLK { j } " , " TILE_CLK " )
2023-06-28 22:50:16 +02:00
tt . create_wire ( f " LSR { j } " , " TILE_LSR " )
2023-06-30 14:44:19 +02:00
tt . create_wire ( f " CE { j } " , " TILE_CE " )
2023-07-05 04:49:25 +02:00
# setup MUX2 wires
2023-07-02 08:09:39 +02:00
for j in range ( 8 ) :
tt . create_wire ( f " OF { j } " , " MUX_OUT " )
tt . create_wire ( f " SEL { j } " , " MUX_SEL " )
tt . create_wire ( " OF30 " , " MUX_OUT " )
2023-07-05 04:49:25 +02:00
# setup ALU wires
for j in range ( 6 ) :
tt . create_wire ( f " CIN { j } " , " ALU_CIN " )
tt . create_wire ( f " COUT { j } " , " ALU_COUT " )
2023-06-28 02:16:29 +02:00
# create logic cells
2023-07-02 08:09:39 +02:00
for i in range ( 8 ) :
2023-06-28 02:16:29 +02:00
# LUT
2023-06-30 09:18:14 +02:00
lut = tt . create_bel ( f " LUT { i } " , " LUT4 " , z = ( i * 2 + 0 ) )
2023-06-28 02:16:29 +02:00
for j , inp_name in enumerate ( lut_inputs ) :
2023-06-28 22:50:16 +02:00
tt . add_bel_pin ( lut , f " I { j } " , f " { inp_name } { i } " , PinType . INPUT )
2023-06-28 02:16:29 +02:00
tt . add_bel_pin ( lut , " F " , f " F { i } " , PinType . OUTPUT )
2025-08-14 13:19:39 +02:00
if i < 6 or " HAS_DFF67 " in db . chip_flags :
2023-10-05 08:18:10 +02:00
tt . create_pip ( f " F { i } " , f " XD { i } " , get_tm_class ( db , f " F { i } " ) )
2024-10-22 12:49:44 +02:00
# also experimental input for FF using SEL wire - this theory will
# allow to place unrelated LUT and FF next to each other
# don't create for now
#tt.create_pip(f"SEL{i}", f"XD{i}", get_tm_class(db, f"SEL{i}"))
2023-06-30 14:44:19 +02:00
# FF
ff = tt . create_bel ( f " DFF { i } " , " DFF " , z = ( i * 2 + 1 ) )
tt . add_bel_pin ( ff , " D " , f " XD { i } " , PinType . INPUT )
tt . add_bel_pin ( ff , " Q " , f " Q { i } " , PinType . OUTPUT )
2025-08-14 05:53:24 +02:00
if i < 6 :
tt . add_bel_pin ( ff , " CLK " , f " CLK { i / / 2 } " , PinType . INPUT )
tt . add_bel_pin ( ff , " SET " , f " LSR { i / / 2 } " , PinType . INPUT )
tt . add_bel_pin ( ff , " RESET " , f " LSR { i / / 2 } " , PinType . INPUT )
tt . add_bel_pin ( ff , " PRESET " , f " LSR { i / / 2 } " , PinType . INPUT )
tt . add_bel_pin ( ff , " CLEAR " , f " LSR { i / / 2 } " , PinType . INPUT )
tt . add_bel_pin ( ff , " CE " , f " CE { i / / 2 } " , PinType . INPUT )
else :
tt . add_bel_pin ( ff , " CLK " , " CLK2 " , PinType . INPUT )
tt . add_bel_pin ( ff , " SET " , " LSR2 " , PinType . INPUT )
tt . add_bel_pin ( ff , " RESET " , " LSR2 " , PinType . INPUT )
tt . add_bel_pin ( ff , " PRESET " , " LSR2 " , PinType . INPUT )
tt . add_bel_pin ( ff , " CLEAR " , " LSR2 " , PinType . INPUT )
tt . add_bel_pin ( ff , " CE " , " CE2 " , PinType . INPUT )
2023-07-05 04:49:25 +02:00
2025-08-14 05:53:24 +02:00
if i < 6 :
2023-07-05 04:49:25 +02:00
# ALU
ff = tt . create_bel ( f " ALU { i } " , " ALU " , z = i + ALU0_Z )
tt . add_bel_pin ( ff , " SUM " , f " F { i } " , PinType . OUTPUT )
tt . add_bel_pin ( ff , " COUT " , f " COUT { i } " , PinType . OUTPUT )
tt . add_bel_pin ( ff , " CIN " , f " CIN { i } " , PinType . INPUT )
# pinout for the ADDSUB ALU mode
tt . add_bel_pin ( ff , " I0 " , f " A { i } " , PinType . INPUT )
tt . add_bel_pin ( ff , " I1 " , f " B { i } " , PinType . INPUT )
tt . add_bel_pin ( ff , " I2 " , f " C { i } " , PinType . INPUT )
tt . add_bel_pin ( ff , " I3 " , f " D { i } " , PinType . INPUT )
2023-07-02 08:09:39 +02:00
# wide luts
for i in range ( 4 ) :
ff = tt . create_bel ( f " MUX { i * 2 } " , " MUX2_LUT5 " , z = MUX20_Z + i * 4 )
tt . add_bel_pin ( ff , " I0 " , f " F { i * 2 } " , PinType . INPUT )
tt . add_bel_pin ( ff , " I1 " , f " F { i * 2 + 1 } " , PinType . INPUT )
tt . add_bel_pin ( ff , " O " , f " OF { i * 2 } " , PinType . OUTPUT )
tt . add_bel_pin ( ff , " S0 " , f " SEL { i * 2 } " , PinType . INPUT )
for i in range ( 2 ) :
ff = tt . create_bel ( f " MUX { i * 4 + 1 } " , " MUX2_LUT6 " , z = MUX21_Z + i * 8 )
tt . add_bel_pin ( ff , " I0 " , f " OF { i * 4 + 2 } " , PinType . INPUT )
tt . add_bel_pin ( ff , " I1 " , f " OF { i * 4 } " , PinType . INPUT )
tt . add_bel_pin ( ff , " O " , f " OF { i * 4 + 1 } " , PinType . OUTPUT )
tt . add_bel_pin ( ff , " S0 " , f " SEL { i * 4 + 1 } " , PinType . INPUT )
ff = tt . create_bel ( f " MUX3 " , " MUX2_LUT7 " , z = MUX23_Z )
tt . add_bel_pin ( ff , " I0 " , f " OF5 " , PinType . INPUT )
tt . add_bel_pin ( ff , " I1 " , f " OF1 " , PinType . INPUT )
tt . add_bel_pin ( ff , " O " , f " OF3 " , PinType . OUTPUT )
tt . add_bel_pin ( ff , " S0 " , f " SEL3 " , PinType . INPUT )
ff = tt . create_bel ( f " MUX7 " , " MUX2_LUT8 " , z = MUX27_Z )
tt . add_bel_pin ( ff , " I0 " , f " OF30 " , PinType . INPUT )
tt . add_bel_pin ( ff , " I1 " , f " OF3 " , PinType . INPUT )
tt . add_bel_pin ( ff , " O " , f " OF7 " , PinType . OUTPUT )
tt . add_bel_pin ( ff , " S0 " , f " SEL7 " , PinType . INPUT )
2023-08-12 07:12:19 +02:00
tdesc . tiletype = tiletype
return tt
2023-07-06 06:48:44 +02:00
2023-08-12 07:12:19 +02:00
def create_ssram_tiletype ( chip : Chip , db : chipdb , x : int , y : int , ttyp : int , tdesc : TypeDesc ) :
2023-07-06 06:48:44 +02:00
# SSRAM is LUT based, so it's logic-like
2023-08-12 07:12:19 +02:00
tt = create_logic_tiletype ( chip , db , x , y , ttyp , tdesc )
2023-07-06 06:48:44 +02:00
lut_inputs = [ ' A ' , ' B ' , ' C ' , ' D ' ]
ff = tt . create_bel ( f " RAM16SDP4 " , " RAM16SDP4 " , z = RAMW_Z )
for i in range ( 4 ) :
tt . add_bel_pin ( ff , f " DI[ { i } ] " , f " { lut_inputs [ i ] } 5 " , PinType . INPUT )
tt . add_bel_pin ( ff , f " WAD[ { i } ] " , f " { lut_inputs [ i ] } 4 " , PinType . INPUT )
2023-07-22 02:01:35 +02:00
# RAD[0] is assumed to be connected to A3, A2, A1 and A0. But
2023-07-06 06:48:44 +02:00
# for now we connect it only to A0, the others will be connected
# directly during packing. RAD[1...3] - similarly.
tt . add_bel_pin ( ff , f " RAD[ { i } ] " , f " { lut_inputs [ i ] } 0 " , PinType . INPUT )
tt . add_bel_pin ( ff , f " DO[ { i } ] " , f " F { i } " , PinType . OUTPUT )
2023-10-03 13:11:40 +02:00
tt . add_bel_pin ( ff , " CLK " , " CLK2 " , PinType . INPUT )
tt . add_bel_pin ( ff , " CE " , " CE2 " , PinType . INPUT )
tt . add_bel_pin ( ff , " WRE " , " LSR2 " , PinType . INPUT )
return tt
# BSRAM
_bsram_inputs = { ' CLK ' , ' OCE ' , ' CE ' , ' RESET ' , ' WRE ' }
def create_bsram_tiletype ( chip : Chip , db : chipdb , x : int , y : int , ttyp : int , tdesc : TypeDesc ) :
typename = " BSRAM "
tiletype = f " { typename } _ { ttyp } "
if tdesc . sfx != 0 :
tiletype + = f " _ { tdesc . sfx } "
tt = chip . create_tile_type ( tiletype )
tt . extra_data = TileExtraData ( chip . strs . id ( typename ) )
portmap = db . grid [ y ] [ x ] . bels [ ' BSRAM ' ] . portmap
bsram = tt . create_bel ( " BSRAM " , " BSRAM " , z = BSRAM_Z )
for sfx in { ' ' , ' A ' , ' B ' } :
for inp in _bsram_inputs :
2024-03-18 13:08:52 +01:00
add_port_wire ( tt , bsram , portmap , f " { inp } { sfx } " , " BSRAM_I " , PinType . INPUT )
2023-10-03 13:11:40 +02:00
for idx in range ( 3 ) :
2024-03-18 13:08:52 +01:00
add_port_wire ( tt , bsram , portmap , f " BLKSEL { sfx } { idx } " , " BSRAM_I " , PinType . INPUT )
2023-10-03 13:11:40 +02:00
for idx in range ( 14 ) :
2024-03-18 13:08:52 +01:00
add_port_wire ( tt , bsram , portmap , f " AD { sfx } { idx } " , " BSRAM_I " , PinType . INPUT )
2023-10-03 13:11:40 +02:00
for idx in range ( 18 ) :
2024-03-18 13:08:52 +01:00
add_port_wire ( tt , bsram , portmap , f " DI { sfx } { idx } " , " BSRAM_I " , PinType . INPUT )
add_port_wire ( tt , bsram , portmap , f " DO { sfx } { idx } " , " BSRAM_O " , PinType . OUTPUT )
2023-10-03 13:11:40 +02:00
if not sfx :
for idx in range ( 18 , 36 ) :
2024-03-18 13:08:52 +01:00
add_port_wire ( tt , bsram , portmap , f " DI { idx } " , " BSRAM_I " , PinType . INPUT )
add_port_wire ( tt , bsram , portmap , f " DO { idx } " , " BSRAM_O " , PinType . OUTPUT )
tdesc . tiletype = tiletype
return tt
# DSP
_mult_inputs = { ' ASEL ' , ' BSEL ' , ' ASIGN ' , ' BSIGN ' }
def create_dsp_tiletype ( chip : Chip , db : chipdb , x : int , y : int , ttyp : int , tdesc : TypeDesc ) :
typename = " DSP "
tiletype = f " { typename } _ { ttyp } "
if tdesc . sfx != 0 :
tiletype + = f " _ { tdesc . sfx } "
tt = chip . create_tile_type ( tiletype )
tt . extra_data = TileExtraData ( chip . strs . id ( typename ) )
# create big DSP
belname = f ' DSP '
portmap = db . grid [ y ] [ x ] . bels [ belname ] . portmap
dsp = tt . create_bel ( belname , " DSP " , DSP_Z )
dsp . flags = BEL_FLAG_HIDDEN
# create DSP macros
for idx in range ( 2 ) :
belname = f ' DSP { idx } '
portmap = db . grid [ y ] [ x ] . bels [ belname ] . portmap
dsp = tt . create_bel ( belname , " DSP " , eval ( f ' DSP_ { idx } _Z ' ) )
dsp . flags = BEL_FLAG_HIDDEN
# create pre-adders
for mac , idx in [ ( mac , idx ) for mac in range ( 2 ) for idx in range ( 4 ) ] :
belname = f ' PADD9 { mac } { idx } '
portmap = db . grid [ y ] [ x ] . bels [ belname ] . portmap
dsp = tt . create_bel ( belname , " PADD9 " , eval ( f ' PADD9_ { mac } _ { idx } _Z ' ) )
add_port_wire ( tt , dsp , portmap , " ADDSUB " , " DSP_I " , PinType . INPUT )
for sfx in { ' A ' , ' B ' } :
for inp in range ( 9 ) :
add_port_wire ( tt , dsp , portmap , f " { sfx } { inp } " , " DSP_I " , PinType . INPUT )
for inp in range ( 9 ) :
add_port_wire ( tt , dsp , portmap , f " C { inp } " , " DSP_I " , PinType . INPUT )
for inp in range ( 4 ) :
add_port_wire ( tt , dsp , portmap , f " CE { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " CLK { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " RESET { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , " ASEL " , " DSP_I " , PinType . INPUT )
for outp in range ( 9 ) :
add_port_wire ( tt , dsp , portmap , f " DOUT { outp } " , " DSP_O " , PinType . OUTPUT )
for mac , idx in [ ( mac , idx ) for mac in range ( 2 ) for idx in range ( 2 ) ] :
belname = f ' PADD18 { mac } { idx } '
portmap = db . grid [ y ] [ x ] . bels [ belname ] . portmap
dsp = tt . create_bel ( belname , " PADD18 " , eval ( f ' PADD18_ { mac } _ { idx } _Z ' ) )
add_port_wire ( tt , dsp , portmap , " ADDSUB " , " DSP_I " , PinType . INPUT )
for sfx in { ' A ' , ' B ' } :
for inp in range ( 18 ) :
add_port_wire ( tt , dsp , portmap , f " { sfx } { inp } " , " DSP_I " , PinType . INPUT )
for inp in range ( 18 ) :
add_port_wire ( tt , dsp , portmap , f " C { inp } " , " DSP_I " , PinType . INPUT )
for inp in range ( 4 ) :
add_port_wire ( tt , dsp , portmap , f " CE { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " CLK { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " RESET { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , " ASEL " , " DSP_I " , PinType . INPUT )
for outp in range ( 18 ) :
add_port_wire ( tt , dsp , portmap , f " DOUT { outp } " , " DSP_O " , PinType . OUTPUT )
# create multipliers
# mult 9x9
for mac , idx in [ ( mac , idx ) for mac in range ( 2 ) for idx in range ( 4 ) ] :
belname = f ' MULT9X9 { mac } { idx } '
portmap = db . grid [ y ] [ x ] . bels [ belname ] . portmap
dsp = tt . create_bel ( belname , " MULT9X9 " , eval ( f ' MULT9X9_ { mac } _ { idx } _Z ' ) )
for sfx in { ' A ' , ' B ' } :
for inp in range ( 9 ) :
add_port_wire ( tt , dsp , portmap , f " { sfx } { inp } " , " DSP_I " , PinType . INPUT )
for inp in _mult_inputs :
add_port_wire ( tt , dsp , portmap , inp , " DSP_I " , PinType . INPUT )
for inp in range ( 4 ) :
add_port_wire ( tt , dsp , portmap , f " CE { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " CLK { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " RESET { inp } " , " DSP_I " , PinType . INPUT )
for outp in range ( 18 ) :
add_port_wire ( tt , dsp , portmap , f " DOUT { outp } " , " DSP_O " , PinType . OUTPUT )
# mult 18x18
for mac , idx in [ ( mac , idx ) for mac in range ( 2 ) for idx in range ( 2 ) ] :
belname = f ' MULT18X18 { mac } { idx } '
portmap = db . grid [ y ] [ x ] . bels [ belname ] . portmap
dsp = tt . create_bel ( belname , " MULT18X18 " , eval ( f ' MULT18X18_ { mac } _ { idx } _Z ' ) )
for sfx in { ' A ' , ' B ' } :
for inp in range ( 18 ) :
add_port_wire ( tt , dsp , portmap , f " { sfx } { inp } " , " DSP_I " , PinType . INPUT )
for inp in _mult_inputs :
add_port_wire ( tt , dsp , portmap , inp , " DSP_I " , PinType . INPUT )
for inp in range ( 4 ) :
add_port_wire ( tt , dsp , portmap , f " CE { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " CLK { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " RESET { inp } " , " DSP_I " , PinType . INPUT )
for outp in range ( 36 ) :
add_port_wire ( tt , dsp , portmap , f " DOUT { outp } " , " DSP_O " , PinType . OUTPUT )
# mult 36x36
belname = ' MULT36X36 '
portmap = db . grid [ y ] [ x ] . bels [ belname ] . portmap
dsp = tt . create_bel ( belname , " MULT36X36 " , MULT36X36_Z )
2024-04-17 06:52:34 +02:00
# LSB 18x18 multipliers sign ports must be zero
add_port_wire ( tt , dsp , db . grid [ y ] [ x ] . bels [ ' MULT18X1800 ' ] . portmap , ' ASIGN ' , " DSP_I " , PinType . INPUT , ' ZERO_ASIGN0 ' )
add_port_wire ( tt , dsp , db . grid [ y ] [ x ] . bels [ ' MULT18X1800 ' ] . portmap , ' BSIGN ' , " DSP_I " , PinType . INPUT , ' ZERO_BSIGN0 ' )
add_port_wire ( tt , dsp , db . grid [ y ] [ x ] . bels [ ' MULT18X1801 ' ] . portmap , ' BSIGN ' , " DSP_I " , PinType . INPUT , ' ZERO_BSIGN1 ' )
add_port_wire ( tt , dsp , db . grid [ y ] [ x ] . bels [ ' MULT18X1810 ' ] . portmap , ' ASIGN ' , " DSP_I " , PinType . INPUT , ' ZERO_ASIGN1 ' )
2024-03-18 13:08:52 +01:00
for i in range ( 2 ) :
for sfx in { ' A ' , ' B ' } :
for inp in range ( 36 ) :
add_port_wire ( tt , dsp , portmap , f " { sfx } { inp } { i } " , " DSP_I " , PinType . INPUT )
for inp in { ' ASIGN ' , ' BSIGN ' } :
add_port_wire ( tt , dsp , portmap , f " { inp } { i } " , " DSP_I " , PinType . INPUT )
for inp in range ( 4 ) :
add_port_wire ( tt , dsp , portmap , f " CE { inp } { i } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " CLK { inp } { i } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " RESET { inp } { i } " , " DSP_I " , PinType . INPUT )
for outp in range ( 72 ) :
add_port_wire ( tt , dsp , portmap , f " DOUT { outp } " , " DSP_O " , PinType . OUTPUT )
# create alus
for mac in range ( 2 ) :
belname = f ' ALU54D { mac } '
portmap = db . grid [ y ] [ x ] . bels [ belname ] . portmap
dsp = tt . create_bel ( belname , " ALU54D " , eval ( f ' ALU54D_ { mac } _Z ' ) )
for sfx in { ' A ' , ' B ' } :
for inp in range ( 54 ) :
add_port_wire ( tt , dsp , portmap , f " { sfx } { inp } " , " DSP_I " , PinType . INPUT )
for inp in { ' ASIGN ' , ' BSIGN ' } :
add_port_wire ( tt , dsp , portmap , inp , " DSP_I " , PinType . INPUT )
for inp in range ( 4 ) :
add_port_wire ( tt , dsp , portmap , f " CE { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " CLK { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " RESET { inp } " , " DSP_I " , PinType . INPUT )
if inp < 2 :
add_port_wire ( tt , dsp , portmap , f " ACCLOAD { inp } " , " DSP_I " , PinType . INPUT )
for outp in range ( 54 ) :
add_port_wire ( tt , dsp , portmap , f " DOUT { outp } " , " DSP_O " , PinType . OUTPUT )
# create multalus
# MULTALU18X18
for mac in range ( 2 ) :
belname = f ' MULTALU18X18 { mac } '
portmap = db . grid [ y ] [ x ] . bels [ belname ] . portmap
dsp = tt . create_bel ( belname , " MULTALU18X18 " , eval ( f ' MULTALU18X18_ { mac } _Z ' ) )
for i in range ( 2 ) :
for sfx in { ' ASIGN ' , ' BSIGN ' } :
add_port_wire ( tt , dsp , portmap , f " { sfx } { i } " , " DSP_I " , PinType . INPUT )
for sfx in { ' A ' , ' B ' } :
for inp in range ( 18 ) :
add_port_wire ( tt , dsp , portmap , f " { sfx } { inp } { i } " , " DSP_I " , PinType . INPUT )
for sfx in { ' C ' , ' D ' } :
for inp in range ( 54 ) :
add_port_wire ( tt , dsp , portmap , f " { sfx } { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , " DSIGN " , " DSP_I " , PinType . INPUT )
for inp in range ( 4 ) :
add_port_wire ( tt , dsp , portmap , f " CE { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " CLK { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " RESET { inp } " , " DSP_I " , PinType . INPUT )
if inp < 2 :
add_port_wire ( tt , dsp , portmap , f " ACCLOAD { inp } " , " DSP_I " , PinType . INPUT )
for outp in range ( 54 ) :
add_port_wire ( tt , dsp , portmap , f " DOUT { outp } " , " DSP_O " , PinType . OUTPUT )
# MULTALU36X18
for mac in range ( 2 ) :
belname = f ' MULTALU36X18 { mac } '
portmap = db . grid [ y ] [ x ] . bels [ belname ] . portmap
dsp = tt . create_bel ( belname , " MULTALU36X18 " , eval ( f ' MULTALU36X18_ { mac } _Z ' ) )
for i in range ( 2 ) :
for sfx in { ' ASIGN ' , ' BSIGN ' } :
add_port_wire ( tt , dsp , portmap , f " { sfx } { i } " , " DSP_I " , PinType . INPUT )
for inp in range ( 18 ) :
add_port_wire ( tt , dsp , portmap , f " A { inp } { i } " , " DSP_I " , PinType . INPUT )
for inp in range ( 7 ) :
add_port_wire ( tt , dsp , portmap , f " ALUSEL { inp } " , " DSP_I " , PinType . INPUT )
for inp in range ( 36 ) :
add_port_wire ( tt , dsp , portmap , f " B { inp } " , " DSP_I " , PinType . INPUT )
for inp in range ( 54 ) :
add_port_wire ( tt , dsp , portmap , f " C { inp } " , " DSP_I " , PinType . INPUT )
for inp in range ( 4 ) :
add_port_wire ( tt , dsp , portmap , f " CE { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " CLK { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " RESET { inp } " , " DSP_I " , PinType . INPUT )
for outp in range ( 54 ) :
add_port_wire ( tt , dsp , portmap , f " DOUT { outp } " , " DSP_O " , PinType . OUTPUT )
# MULTADDALU18X18
for mac in range ( 2 ) :
belname = f ' MULTADDALU18X18 { mac } '
portmap = db . grid [ y ] [ x ] . bels [ belname ] . portmap
dsp = tt . create_bel ( belname , " MULTADDALU18X18 " , eval ( f ' MULTADDALU18X18_ { mac } _Z ' ) )
for i in range ( 2 ) :
for sfx in { ' ASIGN ' , ' BSIGN ' , ' ASEL ' , ' BSEL ' } :
add_port_wire ( tt , dsp , portmap , f " { sfx } { i } " , " DSP_I " , PinType . INPUT )
for inp in range ( 18 ) :
add_port_wire ( tt , dsp , portmap , f " A { inp } { i } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " B { inp } { i } " , " DSP_I " , PinType . INPUT )
for inp in range ( 7 ) :
add_port_wire ( tt , dsp , portmap , f " ALUSEL { inp } " , " DSP_I " , PinType . INPUT )
for inp in range ( 54 ) :
add_port_wire ( tt , dsp , portmap , f " C { inp } " , " DSP_I " , PinType . INPUT )
for inp in range ( 4 ) :
add_port_wire ( tt , dsp , portmap , f " CE { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " CLK { inp } " , " DSP_I " , PinType . INPUT )
add_port_wire ( tt , dsp , portmap , f " RESET { inp } " , " DSP_I " , PinType . INPUT )
for outp in range ( 54 ) :
add_port_wire ( tt , dsp , portmap , f " DOUT { outp } " , " DSP_O " , PinType . OUTPUT )
2023-10-03 13:11:40 +02:00
tdesc . tiletype = tiletype
2023-08-12 07:12:19 +02:00
return tt
2023-06-28 02:16:29 +02:00
2023-07-22 02:01:35 +02:00
# PLL main tile
2023-08-12 07:12:19 +02:00
def create_pll_tiletype ( chip : Chip , db : chipdb , x : int , y : int , ttyp : int , tdesc : TypeDesc ) :
2023-07-22 02:01:35 +02:00
typename = " PLL "
2023-08-06 12:56:08 +02:00
tiletype = f " { typename } _ { ttyp } "
2023-08-12 07:12:19 +02:00
if tdesc . sfx != 0 :
tiletype + = f " _ { tdesc . sfx } "
2023-08-21 11:52:38 +02:00
# disabled PLLs
if tdesc . extra_func and ' disabled ' in tdesc . extra_func and ' PLL ' in tdesc . extra_func [ ' disabled ' ] :
tiletype + = ' _disabled '
tt = chip . create_tile_type ( tiletype )
tt . extra_data = TileExtraData ( chip . strs . id ( typename ) )
tdesc . tiletype = tiletype
return tt
2023-08-06 12:56:08 +02:00
tt = chip . create_tile_type ( tiletype )
2023-07-22 02:01:35 +02:00
tt . extra_data = TileExtraData ( chip . strs . id ( typename ) )
# wires
2025-09-20 07:51:01 +02:00
pll_outputs = { ' CLKOUT ' , ' LOCK ' , ' CLKOUTP ' , ' CLKOUTD ' , ' CLKOUTD3 ' }
2023-07-22 02:01:35 +02:00
if chip . name == ' GW1NS-4 ' :
pll_name = ' PLLVR '
bel_type = ' PLLVR '
else :
pll_name = ' RPLLA '
bel_type = ' rPLL '
portmap = db . grid [ y ] [ x ] . bels [ pll_name ] . portmap
pll = tt . create_bel ( " PLL " , bel_type , z = PLL_Z )
2023-08-06 12:56:08 +02:00
pll . flags = BEL_FLAG_GLOBAL
2023-07-22 02:01:35 +02:00
for pin , wire in portmap . items ( ) :
2025-09-20 07:51:01 +02:00
if pin in pll_outputs :
2023-07-22 02:01:35 +02:00
tt . create_wire ( wire , " PLL_O " )
tt . add_bel_pin ( pll , pin , wire , PinType . OUTPUT )
2025-09-20 07:51:01 +02:00
else :
tt . create_wire ( wire , " PLL_I " )
tt . add_bel_pin ( pll , pin , wire , PinType . INPUT )
2023-08-12 07:12:19 +02:00
tdesc . tiletype = tiletype
return tt
2023-07-22 02:01:35 +02:00
2024-04-07 13:47:23 +02:00
# add Pll's bel to the pad
def add_pll ( chip : Chip , db : chipdb , pad : PadInfo , ioloc : str ) :
try :
if ioloc in db . pad_pll :
row , col , ttyp , bel_name = db . pad_pll [ ioloc ]
pad . extra_data = PadExtraData ( chip . strs . id ( f ' X { col } Y { row } ' ) , chip . strs . id ( bel_name ) , chip . strs . id ( ttyp ) )
except :
return
2023-07-20 04:09:14 +02:00
# pinouts, packages...
2023-07-12 11:36:03 +02:00
_tbrlre = re . compile ( r " IO([TBRL])( \ d+)( \ w) " )
def create_packages ( chip : Chip , db : chipdb ) :
def ioloc_to_tile_bel ( ioloc ) :
side , num , bel_idx = _tbrlre . match ( ioloc ) . groups ( )
if side == ' T ' :
row = 0
col = int ( num ) - 1
elif side == ' B ' :
row = db . rows - 1
col = int ( num ) - 1
elif side == ' L ' :
row = int ( num ) - 1
col = 0
elif side == ' R ' :
row = int ( num ) - 1
col = db . cols - 1
return ( f ' X { col } Y { row } ' , f ' IOB { bel_idx } ' )
created_pkgs = set ( )
for partno_spd , partdata in db . packages . items ( ) :
pkgname , variant , spd = partdata
partno = partno_spd . removesuffix ( spd ) # drop SPEED like 'C7/I6'
if partno in created_pkgs :
continue
2023-07-14 10:57:20 +02:00
created_pkgs . add ( partno )
2023-07-12 11:36:03 +02:00
pkg = chip . create_package ( partno )
2024-10-09 15:16:36 +02:00
if variant in db . sip_cst and pkgname in db . sip_cst [ variant ] :
pkg . extra_data = PackageExtraData ( chip . strs , db . sip_cst [ variant ] [ pkgname ] )
2023-07-12 11:36:03 +02:00
for pinno , pininfo in db . pinout [ variant ] [ pkgname ] . items ( ) :
io_loc , cfgs = pininfo
tile , bel = ioloc_to_tile_bel ( io_loc )
pad_func = " "
for cfg in cfgs :
pad_func + = cfg + " / "
pad_func = pad_func . rstrip ( ' / ' )
bank = int ( db . pin_bank [ io_loc ] )
pad = pkg . create_pad ( pinno , tile , bel , pad_func , bank )
2024-04-07 13:47:23 +02:00
# add PLL if any is connected
add_pll ( chip , db , pad , io_loc )
2023-07-12 11:36:03 +02:00
2023-07-23 08:46:04 +02:00
# Extra chip data
gowin: Himbaechel. Add BSRAM for all chips.
The following primitives are implemented for the GW1N-1, GW2A-18,
GW2AR-18C, GW1NSR-4C, GW1NR-9C, GW1NR-9 and GW1N-4 chips:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
For GW1NSR-4C and GW1NR-9 chips, SP/SPX9 primitives with data widths
of 32/36 bits are implemented using a pair of 16-bit wide
primitives.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-26 11:51:16 +01:00
def create_extra_data ( chip : Chip , db : chipdb , chip_flags : int ) :
2025-09-23 12:42:33 +02:00
chip . extra_data = ChipExtraData ( chip . strs , chip_flags )
if hasattr ( db , " dcs_prefix " ) :
chip . extra_data . set_dcs_prefix ( db . dcs_prefix )
else :
chip . extra_data . set_dcs_prefix ( " CLK " )
2023-07-23 08:46:04 +02:00
chip . extra_data . create_bottom_io ( )
for net_a , net_b in db . bottom_io [ 2 ] :
chip . extra_data . add_bottom_io_cnd ( net_a , net_b )
2023-08-06 12:56:08 +02:00
for diff_type in db . diff_io_types :
chip . extra_data . add_diff_io_type ( diff_type )
2024-09-11 11:18:26 +02:00
# create hclk wire->dhcen bel map
for pip , bel in dhcen_bels . items ( ) :
chip . extra_data . add_dhcen_bel ( pip [ 0 ] , pip [ 1 ] , pip [ 2 ] , bel [ 0 ] , bel [ 1 ] , bel [ 2 ] , bel [ 3 ] )
2024-07-14 08:53:26 +02:00
# create spine->dqce bel map
for spine , bel in dqce_bels . items ( ) :
chip . extra_data . add_dqce_bel ( spine , bel [ 0 ] , bel [ 1 ] , bel [ 2 ] )
# create spine->dcs bel map
for spine , bel in dcs_bels . items ( ) :
chip . extra_data . add_dcs_bel ( spine , bel [ 0 ] , bel [ 1 ] , bel [ 2 ] )
2025-03-19 08:41:35 +01:00
# create iob->dlldly bel map
for io , dlldly in io_dlldly_bels . items ( ) :
chip . extra_data . add_io_dlldly_bel ( io , dlldly )
Gowin. Add a router for segments. (#1456)
Gowin chips have an interesting mechanism - wires that run vertically
through several rows (at least 10) in each column of the chip. In each
row a particular wire has branches to the left and right, covering on
average 4 neighboring cells in the row. For lack of a better term, I
further call such a wire a segment.
So a segment can provide a direct connection in a local rectangle. There
are no special restrictions on the sinks, so segment networks can be
used for ClockEnable, LocalSetReset, as well as for LUT and DFF inputs.
The sources are not so simple - the sources can be the upper or lower
end of the segment, which in theory can lead to unfortunate consequences
if the signal is applied from both ends.
The matter is complicated by the fact that there are default
connections, i.e. in the absence of any set fuse the segment input is
still connected to something (VCC for example) and to disable the unused
end of the segment you need to set a special combination of fuses.
Taking into account which end of which segment is used is one of the
tasks of this router. In addition, segment ends can physically coincide
with PLL, DSP and BSRAM inputs, which can also lead to unexpected
effects. Some of these things are tracked when generating the base, some
in this router, some when packing in gowin_pack.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-03-18 12:02:49 +01:00
# create segments
if hasattr ( db , " segments " ) :
for y_x_idx , seg in db . segments . items ( ) :
_ , x , idx = y_x_idx
chip . extra_data . add_segment ( x , idx , seg [ ' min_x ' ] , seg [ ' min_y ' ] , seg [ ' max_x ' ] , seg [ ' max_y ' ] ,
seg [ ' top_row ' ] , seg [ ' bottom_row ' ] , seg [ ' top_wire ' ] , seg [ ' bottom_wire ' ] ,
seg [ ' top_gate_wire ' ] , seg [ ' bottom_gate_wire ' ] )
# add segment nodes
lt_node = [ NodeWire ( x , seg [ ' top_row ' ] , seg [ ' top_wire ' ] ) ]
lt_node . append ( NodeWire ( x , seg [ ' bottom_row ' ] , seg [ ' bottom_wire ' ] ) )
for row in range ( seg [ ' min_y ' ] , seg [ ' max_y ' ] + 1 ) :
lt_node . append ( NodeWire ( x , row , f ' LT0 { 1 + ( idx / / 4 ) * 3 } ' ) )
node = [ NodeWire ( x , row , f ' LBO { idx / / 4 } ' ) ]
for col in range ( seg [ ' min_x ' ] , seg [ ' max_x ' ] + 1 ) :
node . append ( NodeWire ( col , row , f ' LB { idx } 1 ' ) )
chip . add_node ( node )
chip . add_node ( lt_node )
2023-07-23 08:46:04 +02:00
2023-10-05 08:18:10 +02:00
def create_timing_info ( chip : Chip , db : chipdb . Device ) :
def group_to_timingvalue ( group ) :
# if himbaechel ever recognises unateness, this should match that order.
ff = int ( group [ 0 ] * 1000 )
fr = int ( group [ 1 ] * 1000 )
rr = int ( group [ 2 ] * 1000 )
rf = int ( group [ 3 ] * 1000 )
return TimingValue ( min ( ff , fr , rf , rr ) , max ( ff , fr , rf , rr ) )
speed_grades = [ ]
2025-07-23 08:45:24 +02:00
for speed in db . timing . keys ( ) :
2023-10-05 08:18:10 +02:00
speed_grades . append ( speed )
tmg = chip . set_speed_grades ( speed_grades )
2024-08-17 23:43:07 +02:00
2023-10-05 08:18:10 +02:00
for speed , groups in db . timing . items ( ) :
for group , arc in groups . items ( ) :
if group == " lut " :
lut = tmg . add_cell_variant ( speed , " LUT4 " )
lut . add_comb_arc ( " I0 " , " F " , group_to_timingvalue ( arc [ " a_f " ] ) )
lut . add_comb_arc ( " I1 " , " F " , group_to_timingvalue ( arc [ " b_f " ] ) )
lut . add_comb_arc ( " I2 " , " F " , group_to_timingvalue ( arc [ " c_f " ] ) )
lut . add_comb_arc ( " I3 " , " F " , group_to_timingvalue ( arc [ " d_f " ] ) )
mux5 = tmg . add_cell_variant ( speed , " MUX2_LUT5 " )
mux5 . add_comb_arc ( " I0 " , " O " , group_to_timingvalue ( arc [ " m0_ofx0 " ] ) )
mux5 . add_comb_arc ( " I1 " , " O " , group_to_timingvalue ( arc [ " m1_ofx1 " ] ) )
mux5 . add_comb_arc ( " S0 " , " O " , group_to_timingvalue ( arc [ " fx_ofx1 " ] ) )
mux6 = tmg . add_cell_variant ( speed , " MUX2_LUT6 " )
mux6 . add_comb_arc ( " I0 " , " O " , group_to_timingvalue ( arc [ " m0_ofx0 " ] ) )
mux6 . add_comb_arc ( " I1 " , " O " , group_to_timingvalue ( arc [ " m1_ofx1 " ] ) )
mux6 . add_comb_arc ( " S0 " , " O " , group_to_timingvalue ( arc [ " fx_ofx1 " ] ) )
mux7 = tmg . add_cell_variant ( speed , " MUX2_LUT7 " )
mux7 . add_comb_arc ( " I0 " , " O " , group_to_timingvalue ( arc [ " m0_ofx0 " ] ) )
mux7 . add_comb_arc ( " I1 " , " O " , group_to_timingvalue ( arc [ " m1_ofx1 " ] ) )
mux7 . add_comb_arc ( " S0 " , " O " , group_to_timingvalue ( arc [ " fx_ofx1 " ] ) )
mux8 = tmg . add_cell_variant ( speed , " MUX2_LUT8 " )
mux8 . add_comb_arc ( " I0 " , " O " , group_to_timingvalue ( arc [ " m0_ofx0 " ] ) )
mux8 . add_comb_arc ( " I1 " , " O " , group_to_timingvalue ( arc [ " m1_ofx1 " ] ) )
mux8 . add_comb_arc ( " S0 " , " O " , group_to_timingvalue ( arc [ " fx_ofx1 " ] ) )
elif group == " alu " :
alu = tmg . add_cell_variant ( speed , " ALU " )
alu . add_comb_arc ( " I0 " , " SUM " , group_to_timingvalue ( arc [ " a_f " ] ) )
alu . add_comb_arc ( " I1 " , " SUM " , group_to_timingvalue ( arc [ " b_f " ] ) )
alu . add_comb_arc ( " I3 " , " SUM " , group_to_timingvalue ( arc [ " d_f " ] ) )
alu . add_comb_arc ( " CIN " , " SUM " , group_to_timingvalue ( arc [ " fci_f0 " ] ) )
alu . add_comb_arc ( " I0 " , " COUT " , group_to_timingvalue ( arc [ " a0_fco " ] ) )
alu . add_comb_arc ( " I1 " , " COUT " , group_to_timingvalue ( arc [ " b0_fco " ] ) )
alu . add_comb_arc ( " I3 " , " COUT " , group_to_timingvalue ( arc [ " d0_fco " ] ) )
alu . add_comb_arc ( " CIN " , " COUT " , group_to_timingvalue ( arc [ " fci_fco " ] ) )
elif group == " sram " :
sram = tmg . add_cell_variant ( speed , " RAM16SDP4 " )
for do in range ( 4 ) :
for rad in range ( 4 ) :
sram . add_comb_arc ( f " RAD[ { rad } ] " , f " DO[ { do } ] " , group_to_timingvalue ( arc [ f " rad { rad } _do " ] ) )
sram . add_clock_out ( " CLK " , f " DO[ { do } ] " , ClockEdge . RISING , group_to_timingvalue ( arc [ " clk_do " ] ) )
for di in range ( 4 ) :
sram . add_setup_hold ( " CLK " , f " DI[ { di } " , ClockEdge . RISING , group_to_timingvalue ( arc [ " clk_di_set " ] ) , group_to_timingvalue ( arc [ " clk_di_hold " ] ) )
sram . add_setup_hold ( " CLK " , " WRE " , ClockEdge . RISING , group_to_timingvalue ( arc [ " clk_wre_set " ] ) , group_to_timingvalue ( arc [ " clk_wre_hold " ] ) )
for wad in range ( 4 ) :
sram . add_setup_hold ( " CLK " , f " WAD[ { wad } ] " , ClockEdge . RISING , group_to_timingvalue ( arc [ f " clk_wad { wad } _set " ] ) , group_to_timingvalue ( arc [ f " clk_wad { wad } _hold " ] ) )
elif group == " dff " :
for reset_type in ( ' ' , ' P ' , ' C ' , ' S ' , ' R ' ) :
for clock_enable in ( ' ' , ' E ' ) :
cell_name = " DFF {} {} " . format ( reset_type , clock_enable )
dff = tmg . add_cell_variant ( speed , cell_name )
dff . add_setup_hold ( " CLK " , " D " , ClockEdge . RISING , group_to_timingvalue ( arc [ " di_clksetpos " ] ) , group_to_timingvalue ( arc [ " di_clkholdpos " ] ) )
dff . add_setup_hold ( " CLK " , " CE " , ClockEdge . RISING , group_to_timingvalue ( arc [ " ce_clksetpos " ] ) , group_to_timingvalue ( arc [ " ce_clkholdpos " ] ) )
dff . add_clock_out ( " CLK " , " Q " , ClockEdge . RISING , group_to_timingvalue ( arc [ " clk_qpos " ] ) )
if reset_type in ( ' S ' , ' R ' ) :
port = " RESET " if reset_type == ' R ' else " SET "
dff . add_setup_hold ( " CLK " , port , ClockEdge . RISING , group_to_timingvalue ( arc [ " lsr_clksetpos_syn " ] ) , group_to_timingvalue ( arc [ " lsr_clkholdpos_syn " ] ) )
elif reset_type in ( ' P ' , ' C ' ) :
port = " CLEAR " if reset_type == ' C ' else " PRESET "
dff . add_setup_hold ( " CLK " , port , ClockEdge . RISING , group_to_timingvalue ( arc [ " lsr_clksetpos_asyn " ] ) , group_to_timingvalue ( arc [ " lsr_clkholdpos_asyn " ] ) )
dff . add_comb_arc ( port , " Q " , group_to_timingvalue ( arc [ " lsr_q " ] ) )
cell_name = " DFFN {} {} " . format ( reset_type , clock_enable )
dff = tmg . add_cell_variant ( speed , cell_name )
dff . add_setup_hold ( " CLK " , " D " , ClockEdge . FALLING , group_to_timingvalue ( arc [ " di_clksetneg " ] ) , group_to_timingvalue ( arc [ " di_clkholdneg " ] ) )
dff . add_setup_hold ( " CLK " , " CE " , ClockEdge . FALLING , group_to_timingvalue ( arc [ " ce_clksteneg " ] ) , group_to_timingvalue ( arc [ " ce_clkholdneg " ] ) ) # the DBs have a typo...
dff . add_clock_out ( " CLK " , " Q " , ClockEdge . FALLING , group_to_timingvalue ( arc [ " clk_qneg " ] ) )
if reset_type in ( ' S ' , ' R ' ) :
port = " RESET " if reset_type == ' R ' else " SET "
dff . add_setup_hold ( " CLK " , port , ClockEdge . FALLING , group_to_timingvalue ( arc [ " lsr_clksetneg_syn " ] ) , group_to_timingvalue ( arc [ " lsr_clkholdneg_syn " ] ) )
elif reset_type in ( ' P ' , ' C ' ) :
port = " CLEAR " if reset_type == ' C ' else " PRESET "
dff . add_setup_hold ( " CLK " , port , ClockEdge . FALLING , group_to_timingvalue ( arc [ " lsr_clksetneg_asyn " ] ) , group_to_timingvalue ( arc [ " lsr_clkholdneg_asyn " ] ) )
dff . add_comb_arc ( port , " Q " , group_to_timingvalue ( arc [ " lsr_q " ] ) )
elif group == " bram " :
pass # TODO
elif group == " fanout " :
pass # handled in "wire"
elif group == " glbsrc " :
2024-08-17 23:43:07 +02:00
# no fanout delay for clock wires
for name in [ " CENT_SPINE_PCLK " , " SPINE_TAP_PCLK " , " TAP_BRANCH_PCLK " ] :
tmg . set_pip_class ( speed , name , group_to_timingvalue ( arc [ name ] ) )
tmg . set_pip_class ( speed , ' GCLK_BRANCH ' , group_to_timingvalue ( arc [ ' BRANCH_PCLK ' ] ) )
2023-10-05 08:18:10 +02:00
elif group == " hclk " :
2025-05-13 12:00:30 +02:00
for name in [ ' HclkInMux ' , ' HclkHbrgMux ' , ' HclkOutMux ' , ' HclkDivMux ' ] :
tmg . set_pip_class ( speed , name , group_to_timingvalue ( arc [ name ] ) )
2023-10-05 08:18:10 +02:00
elif group == " iodelay " :
2025-05-13 12:00:30 +02:00
for name in [ ' GI_DO ' , ' SDTAP_DO ' , ' SETN_DO ' , ' VALUE_DO ' , ' SDTAP_DF ' , ' SETN_DF ' , ' VALUE_DF ' ] :
tmg . set_pip_class ( speed , name , group_to_timingvalue ( arc [ name ] ) )
2023-10-05 08:18:10 +02:00
elif group == " wire " :
# wires with delay and fanout delay
for name in [ " X0 " , " X2 " , " X8 " ] :
tmg . set_pip_class ( speed , name , group_to_timingvalue ( arc [ name ] ) , group_to_timingvalue ( groups [ " fanout " ] [ f " { name } Fan " ] ) , TimingValue ( round ( 1e6 / groups [ " fanout " ] [ f " { name } FanNum " ] ) ) )
# wires with delay but no fanout delay
for name in [ " X0CTL " , " X0CLK " , " FX1 " ] :
tmg . set_pip_class ( speed , name , group_to_timingvalue ( arc [ name ] ) )
# wires with presently-unknown delay
2025-05-13 12:00:30 +02:00
for name in [ " LUT_IN " , " DI " , " SEL " , " CIN " , " COUT " , " VCC " , " VSS " , " LW_TAP " , " LW_TAP_0 " , " LW_BRANCH " , " LW_SPAN " , " ISB " ] :
2023-10-05 08:18:10 +02:00
tmg . set_pip_class ( speed , name , TimingValue ( ) )
# wires with fanout-only delay; used on cell output pips
for name , mapping in [ ( " LUT_OUT " , " FFan " ) , ( " FF_OUT " , " QFan " ) , ( " OF " , " OFFan " ) ] :
tmg . set_pip_class ( speed , name , TimingValue ( ) , group_to_timingvalue ( groups [ " fanout " ] [ mapping ] ) , TimingValue ( round ( 1e6 / groups [ " fanout " ] [ f " { mapping } Num " ] ) ) )
2025-10-04 15:02:53 +02:00
# If Apicula does not specify a special location for the global GND and VCC
# sources, place them at X0Y0.
def check_place_VCC_GND ( db : chipdb . Device ) :
for funcs in db . extra_func . values ( ) :
if ' gnd_source ' in funcs or ' vcc_source ' in funcs :
return
db . extra_func . setdefault ( ( 0 , 0 ) , { } ) . update ( { ' gnd_source ' : { } , ' vcc_source ' : { } } )
# *******************************
2023-06-28 02:16:29 +02:00
def main ( ) :
parser = argparse . ArgumentParser ( description = ' Make Gowin BBA ' )
parser . add_argument ( ' -d ' , ' --device ' , required = True )
parser . add_argument ( ' -o ' , ' --output ' , default = " out.bba " )
args = parser . parse_args ( )
device = args . device
with gzip . open ( importlib . resources . files ( " apycula " ) . joinpath ( f " { device } .pickle " ) , ' rb ' ) as f :
db = pickle . load ( f )
gowin: Himbaechel. Add BSRAM for all chips.
The following primitives are implemented for the GW1N-1, GW2A-18,
GW2AR-18C, GW1NSR-4C, GW1NR-9C, GW1NR-9 and GW1N-4 chips:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
For GW1NSR-4C and GW1NR-9 chips, SP/SPX9 primitives with data widths
of 32/36 bits are implemented using a pair of 16-bit wide
primitives.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-26 11:51:16 +01:00
chip_flags = 0 ;
2024-06-23 12:26:50 +02:00
# XXX compatibility
if not hasattr ( db , " chip_flags " ) :
if device not in { " GW1NS-4 " , " GW1N-9 " } :
chip_flags | = CHIP_HAS_SP32 ;
else :
if " HAS_SP32 " in db . chip_flags :
chip_flags | = CHIP_HAS_SP32 ;
if " NEED_SP_FIX " in db . chip_flags :
chip_flags | = CHIP_NEED_SP_FIX ;
2024-06-25 10:27:00 +02:00
if " NEED_BSRAM_OUTREG_FIX " in db . chip_flags :
chip_flags | = CHIP_NEED_BSRAM_OUTREG_FIX ;
2024-06-28 00:15:50 +02:00
if " NEED_BLKSEL_FIX " in db . chip_flags :
chip_flags | = CHIP_NEED_BLKSEL_FIX ;
2024-07-06 11:14:43 +02:00
if " HAS_BANDGAP " in db . chip_flags :
chip_flags | = CHIP_HAS_BANDGAP ;
2025-03-12 09:32:38 +01:00
if " HAS_PLL_HCLK " in db . chip_flags :
chip_flags | = CHIP_HAS_PLL_HCLK ;
if " HAS_CLKDIV_HCLK " in db . chip_flags :
chip_flags | = CHIP_HAS_CLKDIV_HCLK ;
2025-08-07 23:10:18 +02:00
if " HAS_PINCFG " in db . chip_flags :
chip_flags | = CHIP_HAS_PINCFG ;
2025-08-14 13:19:39 +02:00
if " HAS_DFF67 " in db . chip_flags :
chip_flags | = CHIP_HAS_DFF67 ;
2025-08-26 16:17:55 +02:00
if " HAS_CIN_MUX " in db . chip_flags :
chip_flags | = CHIP_HAS_CIN_MUX ;
2025-10-04 15:03:33 +02:00
if " NEED_BSRAM_RESET_FIX " in db . chip_flags :
chip_flags | = CHIP_NEED_BSRAM_RESET_FIX ;
Gowin. Add BSRAM SDP fix. (#1575)
In the GW5A series, the primitive SemiDual Port BSRAM cannot function
when the width of any of the ports is 32/36 bits - it is necessary to
divide one block into two identical ones, each of which will be
responsible for 16 bits.
Here, we perform such a division and, in addition, ensure that the new
cells resulting from the division undergo the same packing procedure as
the original ones.
Naturally, with some reservations (the AUX attribute is responsible for
this) - in the case of SP, when service elements are added, it makes
sense to do this immediately for 32-bit SP and only then divide.
Also, SDPs are currently being corrected for cases where both ports are
‘problematic’, but it may happen that one port is 32 and the other is,
say, 1/2/4/8/16. This has been left for the future.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-10-13 11:07:39 +02:00
if " NEED_SDP_FIX " in db . chip_flags :
chip_flags | = CHIP_NEED_SDP_FIX ;
gowin: Himbaechel. Add BSRAM for all chips.
The following primitives are implemented for the GW1N-1, GW2A-18,
GW2AR-18C, GW1NSR-4C, GW1NR-9C, GW1NR-9 and GW1N-4 chips:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
For GW1NSR-4C and GW1NR-9 chips, SP/SPX9 primitives with data widths
of 32/36 bits are implemented using a pair of 16-bit wide
primitives.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-26 11:51:16 +01:00
2023-06-28 02:16:29 +02:00
X = db . cols ;
Y = db . rows ;
ch = Chip ( " gowin " , device , X , Y )
# Init constant ids
ch . strs . read_constids ( path . join ( path . dirname ( __file__ ) , " constids.inc " ) )
2023-07-12 11:36:03 +02:00
# packages from parntnumbers
create_packages ( ch , db )
2023-06-28 02:16:29 +02:00
# The manufacturer distinguishes by externally identical tiles, so keep
# these differences (in case it turns out later that there is a slightly
# different routing or something like that).
2023-07-25 05:25:33 +02:00
logic_tiletypes = db . tile_types [ ' C ' ]
io_tiletypes = db . tile_types [ ' I ' ]
2023-08-06 12:56:08 +02:00
ssram_tiletypes = db . tile_types [ ' M ' ]
2023-07-25 05:25:33 +02:00
pll_tiletypes = db . tile_types [ ' P ' ]
gowin: Himbaechel. Add GW1NZ-1 BSRAM.
The following primitives are implemented for the GW1NZ-1 chip:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
Also:
- The creation of databases for GW1NS-2 has been removed - this was not
planned to be supported in Himbaechel from the very beginning and
even examples were not created in apicula for this chip due to the
lack of boards with it on sale.
- It is temporarily prohibited to connect DFFs and LUTs into clusters
because for some reason this prevents the creation of images on lower
chips (placer cannot find the placement), although without these
clusters the images are quite working. Requires further research.
- Added creation of ALU with mode 0 - addition. Such an element is not
generated by Yosys, but it is a favorite vendor element and its
support here greatly simplifies the compilation of vendor netlists.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-20 03:27:56 +01:00
bsram_tiletypes = db . tile_types . get ( ' B ' , set ( ) )
2024-03-18 13:08:52 +01:00
dsp_tiletypes = db . tile_types . get ( ' D ' , set ( ) )
2023-08-06 12:56:08 +02:00
2025-10-04 15:02:53 +02:00
# If Apicula does not specify a special location for the global GND and VCC
# sources, place them at X0Y0.
check_place_VCC_GND ( db )
2023-06-28 02:16:29 +02:00
# Setup tile grid
for x in range ( X ) :
for y in range ( Y ) :
ttyp = db . grid [ y ] [ x ] . ttyp
2025-10-04 15:02:53 +02:00
if ttyp in logic_tiletypes :
2023-08-06 12:56:08 +02:00
create_tiletype ( create_logic_tiletype , ch , db , x , y , ttyp )
2023-07-06 06:48:44 +02:00
elif ttyp in ssram_tiletypes :
2023-08-06 12:56:08 +02:00
create_tiletype ( create_ssram_tiletype , ch , db , x , y , ttyp )
2023-06-28 08:32:53 +02:00
elif ttyp in io_tiletypes :
2023-08-06 12:56:08 +02:00
create_tiletype ( create_io_tiletype , ch , db , x , y , ttyp )
2023-07-22 02:01:35 +02:00
elif ttyp in pll_tiletypes :
2023-08-06 12:56:08 +02:00
create_tiletype ( create_pll_tiletype , ch , db , x , y , ttyp )
2023-10-03 13:11:40 +02:00
elif ttyp in bsram_tiletypes :
create_tiletype ( create_bsram_tiletype , ch , db , x , y , ttyp )
2024-03-18 13:08:52 +01:00
elif ttyp in dsp_tiletypes :
create_tiletype ( create_dsp_tiletype , ch , db , x , y , ttyp )
2023-06-28 02:16:29 +02:00
else :
2023-08-06 12:56:08 +02:00
create_tiletype ( create_null_tiletype , ch , db , x , y , ttyp )
2023-06-28 02:16:29 +02:00
# Create nodes between tiles
create_nodes ( ch , db )
gowin: Himbaechel. Add BSRAM for all chips.
The following primitives are implemented for the GW1N-1, GW2A-18,
GW2AR-18C, GW1NSR-4C, GW1NR-9C, GW1NR-9 and GW1N-4 chips:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
For GW1NSR-4C and GW1NR-9 chips, SP/SPX9 primitives with data widths
of 32/36 bits are implemented using a pair of 16-bit wide
primitives.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-26 11:51:16 +01:00
create_extra_data ( ch , db , chip_flags )
2023-10-05 08:18:10 +02:00
create_timing_info ( ch , db )
2023-06-28 02:16:29 +02:00
ch . write_bba ( args . output )
if __name__ == ' __main__ ' :
main ( )