before allowing a new instruction to proceed.
"""
-from nmigen import Elaboratable, Module, Signal
+from nmigen import Elaboratable, Module, Signal, ResetSignal, Cat
from nmigen.cli import rtlil
from nmutil.picker import PriorityPicker
from soc.decoder.power_decoder import create_pdecode
from soc.decoder.power_decoder2 import PowerDecode2
from soc.decoder.decode2execute1 import Data
-from soc.experiment.l0_cache import TstL0CacheBuffer # test only
+from soc.experiment.l0_cache import TstL0CacheBuffer # test only
from soc.config.test.test_loadstore import TestMemPspec
-from soc.decoder.power_enums import InternalOp
+from soc.decoder.power_enums import MicrOp
import operator
+from nmutil.util import rising_edge
+
# helper function for reducing a list of signals down to a parallel
# ORed single signal.
return treereduce(tree, operator.or_, lambda x: getattr(x, attr))
+def ortreereduce_sig(tree):
+ return treereduce(tree, operator.or_, lambda x: x)
+
+
# helper function to place full regs declarations first
def sort_fuspecs(fuspecs):
res = []
for (regname, fspec) in fuspecs.items():
if not regname.startswith("full"):
res.append((regname, fspec))
- return res # enumerate(res)
+ return res # enumerate(res)
class NonProductionCore(Elaboratable):
self.raw_opcode_i = self.pdecode2.dec.raw_opcode_in
# start/stop and terminated signalling
- self.core_start_i = Signal(reset_less=True)
- self.core_stop_i = Signal(reset_less=True)
- self.core_terminated_o = Signal(reset=1) # indicates stopped
+ self.core_stopped_i = Signal(reset_less=True)
+ self.core_reset_i = Signal()
+ self.core_terminate_o = Signal(reset=0) # indicates stopped
def elaborate(self, platform):
m = Module()
regs = self.regs
fus = self.fus.fus
- # core start/stopped state
- core_stopped = Signal(reset=1) # begins in stopped state
-
- # start/stop signalling
- with m.If(self.core_start_i):
- m.d.sync += core_stopped.eq(0)
- with m.If(self.core_stop_i):
- m.d.sync += core_stopped.eq(1)
- m.d.comb += self.core_terminated_o.eq(core_stopped)
-
# connect up Function Units, then read/write ports
- fu_bitdict = self.connect_instruction(m, core_stopped)
+ fu_bitdict = self.connect_instruction(m)
self.connect_rdports(m, fu_bitdict)
self.connect_wrports(m, fu_bitdict)
+ # connect up reset
+ m.d.comb += ResetSignal().eq(self.core_reset_i)
+
return m
- def connect_instruction(self, m, core_stopped):
+ def connect_instruction(self, m):
+ """connect_instruction
+
+ uses decoded (from PowerOp) function unit information from CSV files
+ to ascertain which Function Unit should deal with the current
+ instruction.
+
+ some (such as OP_ATTN, OP_NOP) are dealt with here, including
+ ignoring it and halting the processor. OP_NOP is a bit annoying
+ because the issuer expects busy flag still to be raised then lowered.
+ (this requires a fake counter to be set).
+ """
comb, sync = m.d.comb, m.d.sync
fus = self.fus.fus
dec2 = self.pdecode2
for i, funame in enumerate(fus.keys()):
fu_bitdict[funame] = fu_enable[i]
- # only run when allowed and when instruction is valid
- can_run = Signal(reset_less=True)
- comb += can_run.eq(self.ivalid_i & ~core_stopped)
+ # enable the required Function Unit based on the opcode decode
+ # note: this *only* works correctly for simple core when one and
+ # *only* one FU is allocated per instruction
+ for funame, fu in fus.items():
+ fnunit = fu.fnunit.value
+ enable = Signal(name="en_%s" % funame, reset_less=True)
+ comb += enable.eq((dec2.e.do.fn_unit & fnunit).bool())
+ comb += fu_bitdict[funame].eq(enable)
# sigh - need a NOP counter
counter = Signal(2)
with m.If(counter != 0):
sync += counter.eq(counter - 1)
- comb += self.busy_o.eq(counter != 0)
-
- # check for ATTN: halt if true
- with m.If(self.ivalid_i & (dec2.e.do.insn_type == InternalOp.OP_ATTN)):
- m.d.sync += core_stopped.eq(1)
-
- with m.Elif(self.ivalid_i & (dec2.e.do.insn_type == InternalOp.OP_NOP)):
- sync += counter.eq(2)
comb += self.busy_o.eq(1)
- with m.Else():
- # connect up instructions. only one is enabled at any given time
- for funame, fu in fus.items():
- fnunit = fu.fnunit.value
- enable = Signal(name="en_%s" % funame, reset_less=True)
- comb += enable.eq((dec2.e.do.fn_unit & fnunit).bool() & can_run)
-
- # run this FunctionUnit if enabled, except if the instruction
- # is "attn" in which case we HALT.
- with m.If(enable):
- # route operand, issue, busy, read flags and mask to FU
- comb += fu.oper_i.eq_from_execute1(dec2.e)
- comb += fu.issue_i.eq(self.issue_i)
- comb += self.busy_o.eq(fu.busy_o)
- rdmask = dec2.rdflags(fu)
- comb += fu.rdmaskn.eq(~rdmask)
- comb += fu_bitdict[funame].eq(enable)
+ with m.If(self.ivalid_i): # run only when valid
+ with m.Switch(dec2.e.do.insn_type):
+ # check for ATTN: halt if true
+ with m.Case(MicrOp.OP_ATTN):
+ m.d.sync += self.core_terminate_o.eq(1)
+
+ with m.Case(MicrOp.OP_NOP):
+ sync += counter.eq(2)
+ comb += self.busy_o.eq(1)
+
+ with m.Default():
+ # connect up instructions. only one enabled at a time
+ for funame, fu in fus.items():
+ enable = fu_bitdict[funame]
+
+ # run this FunctionUnit if enabled
+ with m.If(enable):
+ # route op, issue, busy, read flags and mask to FU
+ comb += fu.oper_i.eq_from_execute1(dec2.e)
+ comb += fu.issue_i.eq(self.issue_i)
+ comb += self.busy_o.eq(fu.busy_o)
+ rdmask = dec2.rdflags(fu)
+ comb += fu.rdmaskn.eq(~rdmask)
return fu_bitdict
+ def connect_rdport(self, m, fu_bitdict, rdpickers, regfile, regname, fspec):
+ comb, sync = m.d.comb, m.d.sync
+ fus = self.fus.fus
+ regs = self.regs
+
+ rpidx = regname
+
+ # select the required read port. these are pre-defined sizes
+ print(rpidx, regfile, regs.rf.keys())
+ rport = regs.rf[regfile.lower()].r_ports[rpidx]
+
+ fspecs = fspec
+ if not isinstance(fspecs, list):
+ fspecs = [fspecs]
+
+ rdflags = []
+ pplen = 0
+ reads = []
+ ppoffs = []
+ for i, fspec in enumerate(fspecs):
+ # get the regfile specs for this regfile port
+ (rf, read, write, wid, fuspec) = fspec
+ print ("fpsec", i, fspec, len(fuspec))
+ ppoffs.append(pplen) # record offset for picker
+ pplen += len(fuspec)
+ name = "rdflag_%s_%s_%d" % (regfile, regname, i)
+ rdflag = Signal(name=name, reset_less=True)
+ comb += rdflag.eq(rf)
+ rdflags.append(rdflag)
+ reads.append(read)
+
+ print ("pplen", pplen)
+
+ # create a priority picker to manage this port
+ rdpickers[regfile][rpidx] = rdpick = PriorityPicker(pplen)
+ setattr(m.submodules, "rdpick_%s_%s" % (regfile, rpidx), rdpick)
+
+ for i, fspec in enumerate(fspecs):
+ (rf, read, write, wid, fuspec) = fspec
+ # connect up the FU req/go signals, and the reg-read to the FU
+ # and create a Read Broadcast Bus
+ for pi, (funame, fu, idx) in enumerate(fuspec):
+ pi += ppoffs[i]
+ src = fu.src_i[idx]
+
+ # connect request-read to picker input, and output to go-rd
+ fu_active = fu_bitdict[funame]
+ pick = Signal()
+ comb += pick.eq(fu.rd_rel_o[idx] & fu_active & rdflags[i])
+ print (pick, len(pick))
+ print (rdpick.i, len(rdpick.i), pi)
+ comb += rdpick.i[pi].eq(pick)
+ comb += fu.go_rd_i[idx].eq(rdpick.o[pi])
+
+ # if picked, select read-port "reg select" number to port
+ with m.If(rdpick.o[pi] & rdpick.en_o):
+ comb += rport.ren.eq(reads[i])
+
+ # connect regfile port to input, creating a Broadcast Bus
+ print("reg connect widths",
+ regfile, regname, pi, funame,
+ src.shape(), rport.data_o.shape())
+ # all FUs connect to same port
+ comb += src.eq(rport.data_o)
+
def connect_rdports(self, m, fu_bitdict):
"""connect read ports
fuspecs = byregfiles_rdspec[regfile]
rdpickers[regfile] = {}
+ # argh. an experiment to merge RA and RB in the INT regfile
+ # (we have too many read/write ports)
+ if regfile == 'INT':
+ fuspecs['rbc'] = [fuspecs.pop('rb')]
+ fuspecs['rbc'].append(fuspecs.pop('rc'))
+ if regfile == 'FAST':
+ fuspecs['fast1'] = [fuspecs.pop('fast1')]
+ fuspecs['fast1'].append(fuspecs.pop('fast2'))
+
# for each named regfile port, connect up all FUs to that port
for (regname, fspec) in sort_fuspecs(fuspecs):
- print ("connect rd", regname, fspec)
- rpidx = regname
- # get the regfile specs for this regfile port
- (rf, read, write, wid, fuspec) = fspec
- name = "rdflag_%s_%s" % (regfile, regname)
- rdflag = Signal(name=name, reset_less=True)
- comb += rdflag.eq(rf)
-
- # select the required read port. these are pre-defined sizes
- print (rpidx, regfile, regs.rf.keys())
- rport = regs.rf[regfile.lower()].r_ports[rpidx]
-
- # create a priority picker to manage this port
- rdpickers[regfile][rpidx] = rdpick = PriorityPicker(len(fuspec))
- setattr(m.submodules, "rdpick_%s_%s" % (regfile, rpidx), rdpick)
-
- # connect the regspec "reg select" number to this port
- with m.If(rdpick.en_o):
- comb += rport.ren.eq(read)
-
- # connect up the FU req/go signals, and the reg-read to the FU
- # and create a Read Broadcast Bus
- for pi, (funame, fu, idx) in enumerate(fuspec):
- src = fu.src_i[idx]
-
- # connect request-read to picker input, and output to go-rd
- fu_active = fu_bitdict[funame]
- pick = fu.rd_rel_o[idx] & fu_active & rdflag
- comb += rdpick.i[pi].eq(pick)
- comb += fu.go_rd_i[idx].eq(rdpick.o[pi])
-
- # connect regfile port to input, creating a Broadcast Bus
- print ("reg connect widths",
- regfile, regname, pi, funame,
- src.shape(), rport.data_o.shape())
- comb += src.eq(rport.data_o) # all FUs connect to same port
+ print("connect rd", regname, fspec)
+ self.connect_rdport(m, fu_bitdict, rdpickers, regfile,
+ regname, fspec)
def connect_wrports(self, m, fu_bitdict):
"""connect write ports
fuspecs = byregfiles_wrspec[regfile]
wrpickers[regfile] = {}
for (regname, fspec) in sort_fuspecs(fuspecs):
- print ("connect wr", regname, fspec)
+ print("connect wr", regname, fspec)
rpidx = regname
# get the regfile specs for this regfile port
(rf, read, write, wid, fuspec) = fspec
# select the required write port. these are pre-defined sizes
- print (regfile, regs.rf.keys())
+ print(regfile, regs.rf.keys())
wport = regs.rf[regfile.lower()].w_ports[rpidx]
# create a priority picker to manage this port
- wrpickers[regfile][rpidx] = wrpick = PriorityPicker(len(fuspec))
- setattr(m.submodules, "wrpick_%s_%s" % (regfile, rpidx), wrpick)
+ wrpickers[regfile][rpidx] = wrpick = PriorityPicker(
+ len(fuspec))
+ setattr(m.submodules, "wrpick_%s_%s" %
+ (regfile, rpidx), wrpick)
# connect the regspec write "reg select" number to this port
# only if one FU actually requests (and is granted) the port
# will the write-enable be activated
with m.If(wrpick.en_o):
- sync += wport.wen.eq(write)
+ comb += wport.wen.eq(write)
with m.Else():
- sync += wport.wen.eq(0)
+ comb += wport.wen.eq(0)
# connect up the FU req/go signals and the reg-read to the FU
# these are arbitrated by Data.ok signals
for pi, (funame, fu, idx) in enumerate(fuspec):
# write-request comes from dest.ok
dest = fu.get_out(idx)
+ fu_dest_latch = fu.get_fu_out(idx) # latched output
name = "wrflag_%s_%s_%d" % (funame, regname, idx)
wrflag = Signal(name=name, reset_less=True)
- comb += wrflag.eq(dest.ok)
+ comb += wrflag.eq(dest.ok & fu.busy_o)
- # connect request-read to picker input, and output to go-wr
+ # connect request-write to picker input, and output to go-wr
fu_active = fu_bitdict[funame]
- pick = fu.wr.rel[idx] & fu_active #& wrflag
+ pick = fu.wr.rel_o[idx] & fu_active # & wrflag
comb += wrpick.i[pi].eq(pick)
- sync += fu.go_wr_i[idx].eq(wrpick.o[pi] & wrpick.en_o)
+ # create a single-pulse go write from the picker output
+ wr_pick = Signal()
+ comb += wr_pick.eq(wrpick.o[pi] & wrpick.en_o)
+ comb += fu.go_wr_i[idx].eq(rising_edge(m, wr_pick))
# connect regfile port to input
- print ("reg connect widths",
- regfile, regname, pi, funame,
- dest.shape(), wport.data_i.shape())
- wsigs.append(dest)
+ print("reg connect widths",
+ regfile, regname, pi, funame,
+ dest.shape(), wport.data_i.shape())
+ wsigs.append(fu_dest_latch)
# here is where we create the Write Broadcast Bus. simple, eh?
- sync += wport.data_i.eq(ortreereduce(wsigs, "data"))
+ comb += wport.data_i.eq(ortreereduce_sig(wsigs))
def get_byregfiles(self, readmode):
byregfiles = {}
byregfiles_spec = {}
for (funame, fu) in fus.items():
- print ("%s ports for %s" % (mode, funame))
+ print("%s ports for %s" % (mode, funame))
for idx in range(fu.n_src if readmode else fu.n_dst):
if readmode:
(regfile, regname, wid) = fu.get_in_spec(idx)
else:
(regfile, regname, wid) = fu.get_out_spec(idx)
- print (" %d %s %s %s" % (idx, regfile, regname, str(wid)))
+ print(" %d %s %s %s" % (idx, regfile, regname, str(wid)))
if readmode:
rdflag, read = dec2.regspecmap_read(regfile, regname)
write = None
byregfiles_spec[regfile] = {}
if regname not in byregfiles_spec[regfile]:
byregfiles_spec[regfile][regname] = \
- [rdflag, read, write, wid, []]
+ (rdflag, read, write, wid, [])
# here we start to create "lanes"
if idx not in byregfiles[regfile]:
byregfiles[regfile][idx] = []
# ok just print that out, for convenience
for regfile, spec in byregfiles.items():
- print ("regfile %s ports:" % mode, regfile)
+ print("regfile %s ports:" % mode, regfile)
fuspecs = byregfiles_spec[regfile]
for regname, fspec in fuspecs.items():
[rdflag, read, write, wid, fuspec] = fspec
- print (" rf %s port %s lane: %s" % (mode, regfile, regname))
- print (" %s" % regname, wid, read, write, rdflag)
+ print(" rf %s port %s lane: %s" % (mode, regfile, regname))
+ print(" %s" % regname, wid, read, write, rdflag)
for (funame, fu, idx) in fuspec:
fusig = fu.src_i[idx] if readmode else fu.dest[idx]
- print (" ", funame, fu, idx, fusig)
- print ()
+ print(" ", funame, fu, idx, fusig)
+ print()
return byregfiles, byregfiles_spec