651 lines
30 KiB
Python
651 lines
30 KiB
Python
|
|
# This file is part of the Coriolis Software.
|
|
# Copyright (c) Sorbonne Université 2022-2022, All Rights Reserved
|
|
#
|
|
# +-----------------------------------------------------------------+
|
|
# | C O R I O L I S |
|
|
# | C u m u l u s - P y t h o n T o o l s |
|
|
# | |
|
|
# | Author : Jean-Paul CHAPUT |
|
|
# | E-mail : Jean-Paul.Chaput@lip6.fr |
|
|
# | =============================================================== |
|
|
# | Python : "./plugins/sram_256x32.py" |
|
|
# +-----------------------------------------------------------------+
|
|
|
|
|
|
"""
|
|
Verilog description of ``spram_256x32`` (256 words of 32 bits).
|
|
This descripion is the ASIC part of the SPRAM exctracted from
|
|
FreeCores : ::
|
|
|
|
https://github.com/freecores/ethmac.git
|
|
|
|
.. code:: Verilog
|
|
|
|
module eth_spram_256x32(
|
|
// Generic synchronous single-port RAM interface
|
|
clk, rst, ce, we, oe, addr, di, dato
|
|
|
|
// Generic synchronous single-port RAM interface
|
|
input clk; // Clock, rising edge
|
|
input rst; // Reset, active high
|
|
input ce; // Chip enable input, active high
|
|
input [ 3: 0] we; // Write enable input, active high
|
|
input oe; // Output enable input, active high
|
|
input [ 7: 0] addr; // address bus inputs
|
|
input [31: 0] di; // input data bus
|
|
output [31: 0] dato; // output data bus
|
|
|
|
reg [ 7: 0] mem0 [255:0];
|
|
reg [15: 8] mem1 [255:0];
|
|
reg [23:16] mem2 [255:0];
|
|
reg [31:24] mem3 [255:0];
|
|
wire [31: 0] q;
|
|
reg [ 7: 0] raddr;
|
|
|
|
// Data output drivers
|
|
assign dato = (oe & ce) ? q : {32{1'bz}};
|
|
|
|
// RAM read and write
|
|
// read operation
|
|
always@(posedge clk)
|
|
if (ce)
|
|
raddr <= addr; // read address needs to be registered to read clock
|
|
|
|
assign q = rst ? {32{1'b0}} : { mem3[raddr]
|
|
, mem2[raddr]
|
|
, mem1[raddr]
|
|
, mem0[raddr] };
|
|
|
|
// write operation
|
|
always@(posedge clk)
|
|
begin
|
|
if (ce && we[3]) mem3[addr] <= di[31:24];
|
|
if (ce && we[2]) mem2[addr] <= di[23:16];
|
|
if (ce && we[1]) mem1[addr] <= di[15: 8];
|
|
if (ce && we[0]) mem0[addr] <= di[ 7: 0];
|
|
end
|
|
endmodule
|
|
|
|
|
|
Provisional results
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. note:: All length are in micro-meters.
|
|
|
|
+--------+--------------+-----------------------------+-----------------------------+
|
|
| Arch | Kind | Generator | Yosys |
|
|
+========+==============+=============================+=============================+
|
|
| Mux | # Gates | 23209 (-25.4%) | 32121 |
|
|
+--------+--------------+-----------------------------+ |
|
|
| Nao | # Gates | 34637 (+7.8%) | |
|
|
+--------+--------------+-----------------------------+-----------------------------+
|
|
| 1 Fold |
|
|
+--------+--------------+-----------------------------+-----------------------------+
|
|
| | Area | 7182 x 330 (-5.5%) | 7380 x 340 |
|
|
| Mux +--------------+-----------------------------+-----------------------------+
|
|
| | Wirelength | 1841036 (-4.3%) | 1924153 |
|
|
+--------+--------------+-----------------------------+-----------------------------+
|
|
| | Area | 6680 x 340 (-14.9%) | |
|
|
| Nao +--------------+-----------------------------+ |
|
|
| | Wirelength | 1637781 (-14.9%) | |
|
|
+--------+--------------+-----------------------------+-----------------------------+
|
|
| 2 Fold |
|
|
+--------+--------------+-----------------------------+-----------------------------+
|
|
| | Area | 3599 x 660 (-5.3%) | 3690 x 680 |
|
|
| Mux +--------------+-----------------------------+-----------------------------+
|
|
| | Wirelength | 1670455 (-6.3%) | 1782558 |
|
|
+--------+--------------+-----------------------------+-----------------------------+
|
|
| | Area | 3350 x 680 (-9.2%) | |
|
|
| Nao +--------------+-----------------------------+ |
|
|
| | Wirelength | 1548358 (-13.1%) | |
|
|
+--------+--------------+-----------------------------+-----------------------------+
|
|
| 4 Fold |
|
|
+--------+--------------+-----------------------------+-----------------------------+
|
|
| | Area | 1812 x 1320 (-4.6%) | 1900 x 1320 |
|
|
| Mux +--------------+-----------------------------+-----------------------------+
|
|
| | Wirelength | 1699810 (-1.5%) | 1726436 |
|
|
+--------+--------------+-----------------------------+-----------------------------+
|
|
| | Area | 1692 x 1360 (-8.2%) | |
|
|
| Nao +--------------+-----------------------------+ |
|
|
| | Wirelength | 1512107 (-12.4%) | |
|
|
+--------+--------------+-----------------------------+-----------------------------+
|
|
|
|
|
|
The difference between the two implementations resides only in the *output*
|
|
multiplexer. With a 4 inputs mux made of mux2+mux3 or 2 inputs multiplexer
|
|
made of alternate layers of nand2+nor2.
|
|
|
|
Conclusions for the mux2+mux3 implementation :
|
|
|
|
1. The generator version uses subtantially less gates than the Yosys one.
|
|
As the both SRAM uses the exact same number of SFFs, the difference is
|
|
only due to the decoder for the control of input and output muxes.
|
|
|
|
2. Notwithanding having less gates the generator version uses similar areas,
|
|
which means that we use fewer but significantly *bigger* cells.
|
|
|
|
3. The FlexLib library supplied for SkyWater 130nm do not contains all
|
|
SxLib one, effectively restricting our choices.
|
|
|
|
In particular, to build the output multiplexer we only have mx2 and
|
|
mx3 cells, which are large. The density of the SRAM could be much
|
|
increased if we did have nmx2 and nmx3.
|
|
|
|
Furthermore for the output multiplexers, as it is a controlled case,
|
|
we may also uses three-state drivers cells (which have not been
|
|
ported either).
|
|
|
|
Conclusion for the nand2+nor2 implementation:
|
|
|
|
1. The multiplexer allows us for a much more compact area and noticeably
|
|
lesser wire length. With an increased number of cells (not an issue).
|
|
|
|
2. The total wire length is extremely sensitive to the placement, which
|
|
in our case is just a column ordering. To optimize, the binary tree
|
|
(for the netlist) is not placed fully symmetrically but slightly
|
|
"askew".
|
|
|
|
|
|
.. note:: Cell width in the SkyWater 130 port of FlexLib:
|
|
|
|
============== =====
|
|
Cell Width
|
|
============== =====
|
|
inv_x2 2
|
|
mx2_x2 7
|
|
mx3_x2 11
|
|
a3_x2 5
|
|
nand2_x0 2
|
|
nand3_x0 3
|
|
nand4_x0 4
|
|
nor2_x0 2
|
|
nor3_x0 3
|
|
sff1_x4 15
|
|
============== =====
|
|
|
|
Differrent ways of implementing the output multiplexer :
|
|
|
|
1. mx2_x2 + mx3_x2 = 18
|
|
2. 9 * nand2_x0 = 18
|
|
3. 4 * nand3_x0 + nand4_x0 = 16
|
|
4. 6 * nand2_x0 + nor2_x0 = 14
|
|
"""
|
|
|
|
|
|
import sys
|
|
import re
|
|
import traceback
|
|
import helpers
|
|
from helpers.io import ErrorMessage, WarningMessage
|
|
from helpers.overlay import UpdateSession
|
|
from helpers import trace, l, u, n
|
|
import plugins
|
|
from Hurricane import Breakpoint, DbU, Box, Net, Cell, Instance, \
|
|
Transformation, PythonAttributes
|
|
import CRL
|
|
from plugins.chip.configuration import GaugeConf
|
|
from plugins.sram.sram import Bus, Column, ColBlock, ColGroup, \
|
|
HeaderRow, BaseSRAM
|
|
|
|
|
|
"""
|
|
Simple Standard cells based SRAM generator.
|
|
"""
|
|
|
|
|
|
af = CRL.AllianceFramework.get()
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Class : SRAM_256x32.
|
|
|
|
class SRAM_256x32 ( BaseSRAM ):
|
|
"""
|
|
Build & place a SRAM of 256 words of 32 bits.
|
|
"""
|
|
BIT_GROUP_FMT = 'bit_addr{:04d}_g'
|
|
MUX_GROUP_FMT = 'bits_{}_g'
|
|
|
|
def __init__ ( self, fold, name=None ):
|
|
BaseSRAM.__init__( self, fold )
|
|
if fold == 1:
|
|
pass
|
|
elif fold == 2:
|
|
#self.foldTags = [ 'imux_addr0128' ]
|
|
self.foldTags = [ 'imux_addr0192' ]
|
|
elif fold == 4:
|
|
#self.foldTags = [ 'omux_0_to_127', 'imux_addr0128', 'imux_addr0240' ]
|
|
self.foldTags = [ 'imux_addr0096', 'imux_addr0192', 'imux_addr0160' ]
|
|
else:
|
|
raise ErrorMessage( 1, 'SRAM_256x32.__init__(): Unsupported fold {}, valid values are 1, 2, 4.'.format( fold ))
|
|
if not name:
|
|
name = 'spram_256x32'
|
|
self.cell = af.createCell( name )
|
|
self.mx2Cell = self.confLib.getStdCell( 'mx2_x2' )
|
|
self.mx3Cell = self.confLib.getStdCell( 'mx3_x2' )
|
|
self.na2Cell = self.confLib.getStdCell( 'na2_x1' )
|
|
self.no2Cell = self.confLib.getStdCell( 'no2_x1' )
|
|
with UpdateSession():
|
|
self.buildInterface()
|
|
self.decoder = ColBlock( self, 'decod', 33 )
|
|
for addr in range(256):
|
|
bitGroup = ColGroup( SRAM_256x32.BIT_GROUP_FMT.format( addr ))
|
|
self.rootGroup.group( bitGroup )
|
|
bitGroup.group( Column( self
|
|
, self.mx2Cell
|
|
, 'imux_addr{:04d}'.format( addr )
|
|
, '_byte{byte}_{bbit}'
|
|
, 32 ))
|
|
bitGroup.group( Column( self
|
|
, self.dffCell
|
|
, 'bit_addr{:04d}'.format( addr )
|
|
, '_byte{byte}_{bbit}'
|
|
, 32 ))
|
|
bus = Bus( self, 'imux_addr{:04d}_q({{}})'.format(addr), 32 )
|
|
bitGroup.childs[0].setBusNet( 'q', bus )
|
|
bitGroup.childs[1].setBusNet( 'i', bus )
|
|
bus = Bus( self, 'bit_addr{:04d}_q({{}})'.format(addr), 32 )
|
|
bitGroup.childs[0].setBusNet( 'i0', bus )
|
|
bitGroup.childs[1].setBusNet( 'q', bus )
|
|
bus = Bus( self, 'di({})', 32 )
|
|
bitGroup.childs[0].setBusNet( 'i1', bus )
|
|
bitGroup.childs[1].setCmdNet( 'ck', self.getNet( 'clk' ))
|
|
self.buildDecoder()
|
|
#self._buildOutputMux_mx23()
|
|
self._buildOutputMux_nao23()
|
|
#inst = self.addInstance( 'inv_x2'
|
|
# , 'nrst_inv'
|
|
# , { 'i' : 'rst'
|
|
# , 'nq' : 'n_rst'
|
|
# }
|
|
# )
|
|
#self.decoder.addInstance( 0, inst )
|
|
inst = self.addInstance( 'inv_x2'
|
|
, 'nce_inv'
|
|
, { 'i' : 'ce'
|
|
, 'nq' : 'n_ce'
|
|
}
|
|
)
|
|
self.decoder.addInstance( 0, inst )
|
|
for child in self.rootGroup.childs[0].childs:
|
|
if child.kind == Column.KIND_COLUMN:
|
|
if child.insts[0].getMasterCell() == self.mx3Cell:
|
|
rstCol = Column( self
|
|
, af.getCell( 'a2_x2', CRL.Catalog.State.Views )
|
|
, 'omux_n_rst'
|
|
, '_byte{byte}_{bbit}'
|
|
, 32 )
|
|
busOMux = Bus( self, child.tag+'_q({})', 32 )
|
|
busDato = Bus( self, 'dato({})', 32 )
|
|
child .setBusNet( 'q' , busOMux )
|
|
rstCol.setBusNet( 'i0', busOMux )
|
|
rstCol.setCmdNet( 'i1', self.getNet('n_rst') )
|
|
rstCol.setBusNet( 'q' , busDato )
|
|
self.rootGroup.group( rstCol )
|
|
break
|
|
omuxRoot = self.rootGroup.findChild( 'omux_0_to_255' )
|
|
rstCol = Column( self
|
|
, self.no2Cell
|
|
, 'omux_rst'
|
|
, '_byte{byte}_{bbit}'
|
|
, 32 )
|
|
busOMux = Bus( self, 'omux_0_to_255_nq({})', 32 )
|
|
busDato = Bus( self, 'dato({})', 32 )
|
|
omuxRoot.setBusNet( 'nq', busOMux )
|
|
rstCol.setBusNet( 'i0', busOMux )
|
|
rstCol.setCmdNet( 'i1', self.getNet('rst') )
|
|
rstCol.setBusNet( 'nq', busDato )
|
|
omuxRoot.parent.group( rstCol, after=omuxRoot )
|
|
af.saveCell( self.cell, CRL.Catalog.State.Logical )
|
|
self.cell.updatePlacedFlag()
|
|
|
|
def _buildOutputMux_nao23 ( self ):
|
|
"""
|
|
Build the complete output mux based on successive layers of NAND2
|
|
then NOR2. More compact than the mux based version.
|
|
|
|
Use an "askew" tree to minimize wiring.
|
|
"""
|
|
muxDepth = 0
|
|
levels = [ [], ]
|
|
for addr in range(256):
|
|
oneHotName = 'rdecod_' + self._getDecodNetName( addr, 8 ).replace('_n_','_')
|
|
tag = SRAM_256x32.BIT_GROUP_FMT.format( addr )
|
|
bitGroup = self.rootGroup.findChild(tag)
|
|
bitGroup.unGroup()
|
|
tag = 'sel_' + tag[:-2]
|
|
nand2Col = Column( self
|
|
, self.na2Cell
|
|
, tag
|
|
, '_byte{byte}_{bbit}'
|
|
, 32 )
|
|
nand2Col.setCmdNet( 'i0', self.getNet( oneHotName ))
|
|
busDff = self.getBus( 'bit_addr{:04d}_q({{}})'.format(addr) )
|
|
nand2Col.setBusNet( 'i1', busDff )
|
|
bitGroup.group( nand2Col )
|
|
levels[0].append( (bitGroup, nand2Col ) )
|
|
while len(levels[muxDepth]) > 1:
|
|
levels.append( [] )
|
|
childIndex = 1 if muxDepth else 2
|
|
for i in range(len( levels[muxDepth]) // 2 ):
|
|
naoCell = self.no2Cell if muxDepth%2 else self.na2Cell
|
|
childs = [ levels[muxDepth][i*2][0], levels[muxDepth][i*2+1][0] ]
|
|
leftRoot = levels[muxDepth][i*2 ][1]
|
|
rightRoot = levels[muxDepth][i*2 + 1][1]
|
|
tags = [ childs[0].tag, childs[1].tag ]
|
|
naoTag = SRAM_256x32._mergeOMuxTags( tags )
|
|
naoGroup = ColGroup( naoTag+'_g' )
|
|
trace( 610, ',+', '\tSRAM_256x32._buildOutputmux() {} + {} -> {}\n' \
|
|
.format( tags[0], tags[1], naoTag ))
|
|
nao2Col = Column( self
|
|
, naoCell
|
|
, naoTag
|
|
, '_byte{byte}_{bbit}'
|
|
, 32 )
|
|
naoGroup.group( childs[0] )
|
|
naoGroup.group( childs[1] )
|
|
bus0 = Bus( self, tags[0][:-2]+'_nq({})', 32 )
|
|
bus1 = Bus( self, tags[1][:-2]+'_nq({})', 32 )
|
|
busNao = Bus( self, naoTag+'_nq({})', 32 )
|
|
leftRoot .setBusNet( 'nq', bus0 )
|
|
rightRoot.setBusNet( 'nq', bus1 )
|
|
nao2Col.setBusNet( 'i0', bus0 )
|
|
nao2Col.setBusNet( 'i1', bus1 )
|
|
childs[1].reverse()
|
|
rightRoot.parent.group( nao2Col, before=rightRoot )
|
|
trace( 610, '\tInsert mux {} before {}\n'.format( nao2Col, rightRoot.parent ))
|
|
levels[muxDepth+1].append( (naoGroup, nao2Col) )
|
|
trace( 610, '-,' )
|
|
muxDepth += 1
|
|
self.rootGroup.group( levels[muxDepth][0][0] )
|
|
|
|
def _buildOutputMux_mx23 ( self ):
|
|
"""
|
|
Build the complete output mux based on successive layers of mux4,
|
|
each mux4 beeing built upon a mux2 + mux3 (_doMux4_mux23).
|
|
"""
|
|
omuxGroupsCurr = []
|
|
omuxGroupsNext = []
|
|
muxDepth = 0
|
|
for i in range(256//4):
|
|
childs = []
|
|
for addr in range(i*4, (i+1)*4):
|
|
tag = SRAM_256x32.BIT_GROUP_FMT.format( addr )
|
|
childs.append( self.rootGroup.findChild( tag ))
|
|
childs[-1].unGroup()
|
|
omuxGroupsCurr.append( self._doMux4_mx23( childs, muxDepth ))
|
|
while len(omuxGroupsCurr) >= 4:
|
|
trace( 610, '\tGrouping {} elements.\n'.format( len(omuxGroupsCurr )))
|
|
muxDepth += 1
|
|
for i in range(len(omuxGroupsCurr)//4):
|
|
omuxGroupsNext.append( self._doMux4_mx23( omuxGroupsCurr[i*4:(i+1)*4], muxDepth ))
|
|
omuxGroupsCurr = omuxGroupsNext
|
|
omuxGroupsNext = []
|
|
for group in omuxGroupsCurr:
|
|
self.rootGroup.group( group )
|
|
|
|
def _doMux4_mx23 ( self, childs, muxDepth ):
|
|
"""
|
|
Build a 4 entry mux. It uses a mux2 / mux3 combination.
|
|
Returns a newly build group.
|
|
Entry selection given (cmd0,cmd1) : ::
|
|
|
|
00 ==> i0 (mux2.i0)
|
|
01 ==> i1 (mux2.i1)
|
|
10 ==> i2 (mux3.i2)
|
|
11 ==> i3 (mux3.i1)
|
|
"""
|
|
tags = []
|
|
for child in childs:
|
|
tags.append( child.tag )
|
|
childIndex = 1 if muxDepth == 0 else 4
|
|
muxTag = SRAM_256x32._mergeOMuxTags( tags )
|
|
mux2Tag = SRAM_256x32._mergeOMuxTags( tags[0:2] )
|
|
mux3Tag = SRAM_256x32._mergeOMuxTags( tags )
|
|
muxGroup = ColGroup( muxTag+'_g' )
|
|
trace( 610, ',+', '\tSRAM_256x32._doMux4_mx23() {} + {} -> {}\n' \
|
|
.format( mux2Tag, mux3Tag, muxTag ))
|
|
mux2Col = Column( self
|
|
, self.mx2Cell
|
|
, mux2Tag
|
|
, '_byte{byte}_{bbit}'
|
|
, 32 )
|
|
mux2Col.setCmdNet( 'cmd', self.getNet( 'raddr({})'.format(muxDepth*2) ))
|
|
mux3Col = Column( self
|
|
, self.mx3Cell
|
|
, mux3Tag
|
|
, '_byte{byte}_{bbit}'
|
|
, 32 )
|
|
mux3Col.setCmdNet( 'cmd0', self.getNet( 'raddr({})'.format(muxDepth*2 + 1) ))
|
|
mux3Col.setCmdNet( 'cmd1', self.getNet( 'raddr({})'.format(muxDepth*2 ) ))
|
|
muxGroup.group( childs[0] )
|
|
muxGroup.group( mux2Col )
|
|
muxGroup.group( childs[1] )
|
|
muxGroup.group( childs[2] )
|
|
muxGroup.group( mux3Col )
|
|
muxGroup.group( childs[3] )
|
|
bus0 = Bus( self, tags[0][:-2]+'_q({})', 32 )
|
|
bus1 = Bus( self, tags[1][:-2]+'_q({})', 32 )
|
|
bus2 = Bus( self, tags[2][:-2]+'_q({})', 32 )
|
|
bus3 = Bus( self, tags[3][:-2]+'_q({})', 32 )
|
|
busMx2 = Bus( self, mux2Tag+'_q({})', 32 )
|
|
childs[0].childs[ childIndex ].setBusNet( 'q', bus0 )
|
|
childs[1].childs[ childIndex ].setBusNet( 'q', bus1 )
|
|
childs[2].childs[ childIndex ].setBusNet( 'q', bus2 )
|
|
childs[3].childs[ childIndex ].setBusNet( 'q', bus3 )
|
|
mux2Col.setBusNet( 'i0', bus0 )
|
|
mux2Col.setBusNet( 'i1', bus1 )
|
|
mux2Col.setBusNet( 'q' , busMx2 )
|
|
mux3Col.setBusNet( 'i0', busMx2 )
|
|
mux3Col.setBusNet( 'i2', bus2 )
|
|
mux3Col.setBusNet( 'i1', bus3 )
|
|
childs[1].reverse()
|
|
childs[3].reverse()
|
|
trace( 610, '-,' )
|
|
return muxGroup
|
|
|
|
@staticmethod
|
|
def _mergeOMuxTags ( tags ):
|
|
"""
|
|
Merge two output mux column tags. We assume that we merge only
|
|
contiguous tags.
|
|
|
|
Example: ::
|
|
|
|
'omux_0_to_1' + 'omux_2_to_3' ==> 'omux_0_to_3'
|
|
"""
|
|
vectorRe = re.compile( '^omux_(?P<lsb>\d+)_to_(?P<msb>\d+)' )
|
|
addrs = []
|
|
for tag in tags:
|
|
end = -2 if tag.endswith('_g') else 0
|
|
if tag.startswith('bit_addr'):
|
|
addrs.append( int( tag[8:end] ))
|
|
if tag.startswith('sel_bit_addr'):
|
|
addrs.append( int( tag[12:end] ))
|
|
elif tag.startswith('omux'):
|
|
m = vectorRe.match( tag )
|
|
addrs += [ int(m.group('lsb')), int(m.group('msb')) ]
|
|
addrs.sort()
|
|
omuxTag = 'omux'
|
|
omuxTag = 'omux_{}_to_{}'.format( addrs[0], addrs[-1] )
|
|
return omuxTag
|
|
|
|
def buildInterface ( self ):
|
|
""" Build the interface of the SRAM. """
|
|
self.addExternalNet( 'clk', Net.Direction.DirIn, Net.Type.CLOCK )
|
|
self.addExternalNet( 'rst', Net.Direction.DirIn )
|
|
self.addExternalNet( 'ce' , Net.Direction.DirIn )
|
|
for bit in range(4):
|
|
self.addExternalNet( 'we({})'.format(bit) , Net.Direction.DirIn )
|
|
self.addExternalNet( 'oe' , Net.Direction.DirIn )
|
|
for bit in range(8):
|
|
self.addExternalNet( 'addr({})'.format(bit) , Net.Direction.DirIn )
|
|
for bit in range(32):
|
|
self.addExternalNet( 'di({})'.format(bit) , Net.Direction.DirIn )
|
|
for bit in range(32):
|
|
self.addExternalNet( 'dato({})'.format(bit) , Net.Direction.DirOut )
|
|
self.addExternalNet( 'vdd' , Net.Direction.DirIn, Net.Type.POWER )
|
|
self.addExternalNet( 'vss' , Net.Direction.DirIn, Net.Type.GROUND )
|
|
|
|
def _getDecodNetName ( self, oneHot, addrWidth ):
|
|
"""
|
|
Build a net name for a particular oneHot bit in the range covered by addrWidth.
|
|
The first part is the address lines and the second the value they decod.
|
|
If the oneHot value exceed 2^addrWidth, we uses the *next* address lines.
|
|
|
|
======== =========== ========================
|
|
oneHot addrWidth net name
|
|
======== =========== ========================
|
|
0 4 'decod_3_2_1_0_0000'
|
|
1 4 'decod_3_2_1_0_0001'
|
|
2 4 'decod_3_2_1_0_0010'
|
|
3 4 'decod_3_2_1_0_0011'
|
|
4 4 'decod_3_2_1_0_0100'
|
|
15 4 'decod_3_2_1_0_1111'
|
|
16 4 'decod_7_6_5_4_0000'
|
|
17 4 'decod_7_6_5_4_0001'
|
|
======== =========== ========================
|
|
"""
|
|
netName = ''
|
|
indexFirstBit = (oneHot >> addrWidth) * addrWidth
|
|
for bit in range(indexFirstBit, indexFirstBit + addrWidth):
|
|
netName = '{}_'.format(str( bit )) + netName
|
|
divider = 1 << addrWidth
|
|
netName = '{}{:0{width}b}'.format( netName, oneHot % divider, width=addrWidth )
|
|
return netName
|
|
|
|
def _getDecodInstConf ( self, oneHot, addrWidth ):
|
|
"""
|
|
Compute the informations needed to instanciate one cell of one level of
|
|
the decoder. For the first level of one hot (addrWidth == 2), the inputs
|
|
are just direct or inverted addresses bits. For the upper level we
|
|
combine the outputs of the previous one hot level, that is the one with
|
|
addrWidth/2 to generate the current one.
|
|
"""
|
|
instConf = []
|
|
if addrWidth == 2:
|
|
indexFirstBit = (oneHot >> addrWidth) * addrWidth
|
|
valueAddr = oneHot % (1 << addrWidth)
|
|
trunkName = 'n_'+self._getDecodNetName( oneHot, addrWidth )
|
|
instConf.append( self.confLib.getStdCellName('na2_x1') )
|
|
instConf.append( 'decod_nand2_{}'.format( trunkName ))
|
|
instConf.append( {} )
|
|
for i in range(2):
|
|
inv = '' if (valueAddr & (1 << i)) else 'n_'
|
|
instConf[2][ 'i{}'.format(i) ] = '{}addr({})'.format( inv, indexFirstBit+i )
|
|
instConf[2][ 'nq' ] = 'decod_'+trunkName
|
|
elif addrWidth == 4 or addrWidth == 8:
|
|
halfWidth = addrWidth>>1
|
|
halfMask = 0
|
|
for i in range(halfWidth):
|
|
halfMask |= 1 << i
|
|
indexFirstBit = (oneHot >> addrWidth) * addrWidth
|
|
valueAddr = oneHot % (1 << addrWidth)
|
|
trunkName = self._getDecodNetName( oneHot, addrWidth )
|
|
gate = 'no2_x1' if addrWidth == 4 else 'na2_x1'
|
|
instConf.append( self.confLib.getStdCellName(gate) )
|
|
instConf.append( 'decod_{}_{}'.format( gate[:-3], trunkName ))
|
|
instConf.append( {} )
|
|
offset = (oneHot >> addrWidth) << (halfWidth+1)
|
|
oneHot0 = (oneHot & halfMask) + offset
|
|
instConf[2][ 'i0' ] = 'decod_n_'+self._getDecodNetName( oneHot0, halfWidth )
|
|
oneHot1 = ((oneHot >> halfWidth) & halfMask) + (1<<(halfWidth)) + offset
|
|
instConf[2][ 'i1' ] = 'decod_n_'+self._getDecodNetName( oneHot1, halfWidth )
|
|
instConf[2][ 'nq' ] = 'decod_n_'+trunkName
|
|
trace( 610, '\t{:08b} {:3d}:{} + {:3d}:{} => {:3d}::{:08b}:{}\n' \
|
|
.format( halfMask
|
|
, oneHot0, self._getDecodNetName( oneHot0, halfWidth )
|
|
, oneHot1, self._getDecodNetName( oneHot1, halfWidth )
|
|
, oneHot , oneHot, trunkName ))
|
|
return instConf
|
|
|
|
def buildDecoder ( self ):
|
|
trace( 610, ',+', '\tSRAM_256x32.buildDecoder()\n' )
|
|
for bit in range(8):
|
|
inst = self.addInstance( 'mx2_x2'
|
|
, 'raddr_imux_{}'.format(bit)
|
|
, { 'cmd' : 'ce'
|
|
, 'i0' : 'raddr({})'.format(bit)
|
|
, 'i1' : 'addr({})'.format(bit)
|
|
, 'q' : 'raddr_imux_q({})'.format(bit)
|
|
}
|
|
)
|
|
self.decoder.addInstance( bit * 4 + 1, inst )
|
|
inst = self.addInstance( 'sff1_x4'
|
|
, 'raddr_sff_{}'.format(bit)
|
|
, { 'i' : 'raddr_imux_q({})'.format(bit)
|
|
, 'q' : 'raddr({})'.format(bit)
|
|
}
|
|
)
|
|
self.decoder.addInstance( bit * 4, inst )
|
|
self.connect( 'raddr_sff_{}'.format(bit), 'ck', 'clk' )
|
|
for we in range(4):
|
|
inst = self.addInstance( 'inv_x1'
|
|
, 'decod_n_we_{}'.format(we)
|
|
, { 'i' : 'we({})'.format(we)
|
|
, 'nq' : 'n_we({})'.format(we)
|
|
}
|
|
)
|
|
self.decoder.addInstance( we*4 + 1, inst )
|
|
for bit in range(8):
|
|
inst = self.addInstance( 'inv_x1'
|
|
, 'decod_inv_{}'.format(bit)
|
|
, { 'i' : 'addr({})'.format(bit)
|
|
, 'nq' : 'n_addr({})'.format(bit)
|
|
}
|
|
)
|
|
self.decoder.addInstance( bit*4 + 1, inst )
|
|
for oneHot in range(16):
|
|
trace( 610, '\t{}\n'.format( self._getDecodNetName(oneHot,2) ))
|
|
instDatas = self._getDecodInstConf( oneHot, 2 )
|
|
print( instDatas )
|
|
inst = self.addInstance( instDatas[0], instDatas[1], instDatas[2] )
|
|
self.decoder.addInstance( oneHot*2 + 1, inst )
|
|
for oneHot in range(32):
|
|
instDatas = self._getDecodInstConf( oneHot, 4 )
|
|
inst = self.addInstance( instDatas[0], instDatas[1], instDatas[2] )
|
|
self.decoder.addInstance( oneHot + (oneHot+1)%2, inst )
|
|
for oneHot in range(256):
|
|
bitTag = 'bit_addr{:04d}'.format( oneHot )
|
|
imuxTag = 'imux_addr{:04d}'.format( oneHot )
|
|
instDatas = self._getDecodInstConf( oneHot, 8 )
|
|
inst = self.addInstance( instDatas[0], instDatas[1], instDatas[2] )
|
|
dffCol = self.rootGroup.findChild( bitTag )
|
|
imuxCol = self.rootGroup.findChild( imuxTag )
|
|
self.toHeaders.append(( inst, imuxCol.insts[0], 0 ))
|
|
for we in range(4):
|
|
cmdNetName = 'decod_addr{:04d}_we({})'.format( oneHot, we )
|
|
inst = self.addInstance( self.confLib.getStdCellName('no3_x1')
|
|
, 'decod_no3_we_{}_{}'.format(we,oneHot)
|
|
, { 'i0' : instDatas[2]['nq']
|
|
, 'i1' : 'n_ce'
|
|
, 'i2' : 'n_we({})'.format(we)
|
|
, 'nq' : cmdNetName
|
|
}
|
|
)
|
|
self.toHeaders.append(( inst, imuxCol.insts[0], 0 ))
|
|
for bit in range(8):
|
|
self.connect( 'imux_addr{:04d}_byte{byte}_{bbit}'.format( oneHot, byte=we, bbit=bit )
|
|
, 'cmd'
|
|
, cmdNetName
|
|
)
|
|
oneHotName = instDatas[2]['nq'].replace('_n_','_')
|
|
inst = self.addInstance( 'inv_x1'
|
|
, 'omux_onehot_inv_{:04d}'.format(oneHot)
|
|
, { 'i' : instDatas[2]['nq']
|
|
, 'nq' : oneHotName
|
|
}
|
|
)
|
|
self.toHeaders.append(( inst, imuxCol.insts[0], 0))
|
|
sffName = 'omux_onehot_dff_{:04d}'.format(oneHot)
|
|
inst = self.addInstance( 'sff1_x4'
|
|
, sffName
|
|
, { 'i' : oneHotName
|
|
, 'q' : 'r'+oneHotName
|
|
}
|
|
)
|
|
self.connect( sffName, 'ck', 'clk' )
|
|
self.toHeaders.append(( inst, imuxCol.insts[0], 1 ))
|
|
trace( 610, '-,' )
|