Skip to content

Instantly share code, notes, and snippets.

@Ravenslofty
Created July 13, 2020 18:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ravenslofty/113c9ec41ab7d278e54bc3a394d7de07 to your computer and use it in GitHub Desktop.
Save Ravenslofty/113c9ec41ab7d278e54bc3a394d7de07 to your computer and use it in GitHub Desktop.
from nmigen import *
from nmigen.back import verilog
class DPLUT(Elaboratable):
def __init__(self, width):
assert width > 1
self.width = width
self.i_lut = Signal()
self.i_luten = Signal()
self.o_lut = Signal()
self.i_sel = Signal(width)
self.i_mode = Signal()
self.o_prop = Signal()
self.o_gen = Signal()
def elaborate(self, platform):
m = Module()
data = Signal(2**self.width)
# Shift register as backing store for LUT.
with m.If(self.i_luten):
m.d.sync += data.eq((data << 1) | self.i_lut)
m.d.comb += self.o_lut.eq(data[-1])
# Propagate mux tree
p = Signal()
with m.Switch(self.i_sel[:-1]):
for i in range(2**(self.width-1)):
with m.Case(i):
m.d.comb += p.eq(data[i])
# Generate mux tree
g = Signal()
with m.Switch(self.i_sel[:-1]):
for i in range(2**(self.width-1)):
with m.Case(i):
m.d.comb += g.eq(data[i + 2**(self.width-1)])
# Mode mux
with m.Switch(self.i_mode):
with m.Case(0): # Arithmetic mode
m.d.comb += self.o_prop.eq(p)
m.d.comb += self.o_gen.eq(g)
with m.Case(1): # Normal mode
m.d.comb += self.o_prop.eq(Mux(self.i_sel[-1], p, g))
m.d.comb += self.o_gen.eq(0)
return m
class Carry(Elaboratable):
def __init__(self, width):
assert width > 1
self.width = width
self.i_cin = Signal()
self.i_prop = Signal(width)
self.i_gen = Signal(width)
self.o_sum = Signal(width)
self.o_cout = Signal()
def elaborate(self, platform):
m = Module()
carry = Signal(self.width)
# Carry chain
m.d.comb += carry[0].eq((self.i_cin & self.i_prop[0]) | self.i_gen[0])
for i in range(1, self.width):
m.d.comb += carry[i].eq((carry[i-1] & self.i_prop[i]) | self.i_gen[i])
m.d.comb += self.o_cout.eq(carry[3])
# Sum chain
m.d.comb += self.o_sum[0].eq(self.i_prop[0] ^ self.i_cin)
for i in range(1, self.width):
m.d.comb += self.o_sum[i].eq(self.i_prop[i] ^ carry[i-1])
return m
class Flop(Elaboratable):
def __init__(self):
self.i_glbrstn = Signal() # Negative-true global reset
self.i_aload = Signal() # Asynchronous load
self.i_aloaden = Signal() # Asynchronous load enable
self.i_clocken = Signal() # Clock enable
self.i_data = Signal() # Data input
self.o_q = Signal() # Data output
def elaborate(self, platform):
m = Module()
data = Signal()
reset = Signal()
set_ = Signal()
m.d.comb += [
data.eq(Mux(self.i_clocken, self.i_data, self.o_q)),
reset.eq(self.i_glbrstn & ~(self.i_aloaden & self.i_aload)),
set_.eq(~(self.i_aloaden & self.i_aload)),
]
# You can't emulate flops in nMigen, so we use a Yosys cell instead.
m.submodules.flop = Instance("$dffsr",
p_WIDTH=1,
p_CLK_POLARITY=1,
p_SET_POLARITY=0,
p_CLR_POLARITY=0,
i_CLK=ClockSignal("sync"),
i_SET=set_,
i_CLR=reset,
i_D=data,
o_Q=self.o_q
)
return m
class LogicModule(Elaboratable):
def __init__(self, lut_width, lut_count):
self.lut_width = lut_width
self.lut_count = lut_count
self.i_lut = Signal()
self.i_luten = Signal()
self.i_lutmode = Signal()
self.i_lutsel = Signal(lut_width * lut_count)
self.i_carry = Signal()
self.o_carry = Signal()
self.i_glbrstn = Signal()
self.i_aload = Signal()
self.i_aloaden = Signal()
self.i_clocken = Signal()
self.i_reg = Signal(lut_count)
self.i_regsel = Signal(lut_count)
self.o_comb = Signal(lut_count)
self.o_reg = Signal(lut_count)
def elaborate(self, platform):
m = Module()
# Instantiate all the logic.
luts = []
flops = []
for n in range(self.lut_count):
lut = DPLUT(self.lut_width)
luts.append(lut)
m.submodules["lut_{}".format(n)] = lut
flop = Flop()
flops.append(flop)
m.submodules["flop_{}".format(n)] = flop
m.submodules.carry = carry = Carry(self.lut_count)
# LUT shift register
m.d.comb += luts[0].i_lut.eq(self.i_lut)
m.d.comb += luts[0].i_luten.eq(self.i_luten)
for n in range(1, self.lut_count):
m.d.comb += luts[n].i_lut.eq(luts[n-1].o_lut)
m.d.comb += luts[n].i_luten.eq(self.i_luten)
# LUT mode (per LM)
for n in range(self.lut_count):
m.d.comb += luts[n].i_mode.eq(self.i_lutmode)
# Input -> LUT
for n in range(self.lut_count):
m.d.comb += luts[n].i_sel.eq(self.i_lutsel[n * self.lut_width : (n + 1) * self.lut_width])
# LUT -> Carry
for n in range(self.lut_count):
m.d.comb += carry.i_prop[n].eq(luts[n].o_prop)
m.d.comb += carry.i_gen[n].eq(luts[n].o_gen)
# Carry in/carry out
m.d.comb += carry.i_cin.eq(self.i_carry)
m.d.comb += self.o_carry.eq(carry.o_cout)
# Carry -> FF
for n in range(self.lut_count):
m.d.comb += [
flops[n].i_glbrstn.eq(self.i_glbrstn),
flops[n].i_aload.eq(self.i_aload),
flops[n].i_aloaden.eq(self.i_aloaden),
flops[n].i_clocken.eq(self.i_clocken),
flops[n].i_data.eq(Mux(self.i_regsel, self.i_reg, carry.o_sum[n])),
self.o_reg[n].eq(flops[n].o_q),
self.o_comb[n].eq(carry.o_sum[n]),
]
return m
lm = LogicModule(4, 4)
ports = [
lm.i_lut,
lm.i_luten,
lm.i_lutmode,
lm.i_lutsel,
lm.i_carry,
lm.o_carry,
lm.i_glbrstn,
lm.i_aload,
lm.i_aloaden,
lm.i_clocken,
lm.o_comb,
lm.o_reg,
]
print(verilog.convert(lm, ports=ports))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment