-Subproject commit b444d0968ca397a04325b5a17b617807ba0bfedc
+Subproject commit 1b3c9590a8878194b8df77c10d184f26f2670ee8
self.lk = Signal(reset_less=True)
self.rc = Data(1, "rc")
self.oe = Data(1, "oe")
- self.invert_a = Signal(reset_less=True)
+ self.invert_in = Signal(reset_less=True)
self.zero_a = Signal(reset_less=True)
self.input_carry = Signal(CryIn, reset_less=True)
self.output_carry = Signal(reset_less=True)
pdecode2 = m.submodules.pdecode2
dec = pdecode2.dec
e = pdecode2.e
- comb += Assert(e.invert_a == dec.op.inv_a)
+ comb += Assert(e.invert_in == dec.op.inv_a)
comb += Assert(e.invert_out == dec.op.inv_out)
comb += Assert(e.input_carry == dec.op.cry_in)
comb += Assert(e.output_carry == dec.op.cry_out)
self.namespace['CA32'] = self.spr['XER'][XER_bits['CA32']].value
def handle_carry_(self, inputs, outputs, already_done):
- inv_a = yield self.dec2.e.do.invert_a
+ inv_a = yield self.dec2.e.do.invert_in
if inv_a:
inputs[0] = ~inputs[0]
self.spr['XER'][XER_bits['CA32']] = cy32
def handle_overflow(self, inputs, outputs, div_overflow):
- inv_a = yield self.dec2.e.do.invert_a
+ inv_a = yield self.dec2.e.do.invert_in
if inv_a:
inputs[0] = ~inputs[0]
"""Cascading Power ISA Decoder
+License: LGPLv3
+
+# Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+# Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
+
This module uses CSV tables in a hierarchical/peer cascading fashion,
to create a multi-level instruction decoder by recognising appropriate
patterns. The output is a wide, flattened (1-level) series of bitfields,
),
]
+
"""
from collections import namedtuple
# decoded/selected instruction flags
comb += do.data_len.eq(op.ldst_len)
- comb += do.invert_a.eq(op.inv_a)
+ comb += do.invert_in.eq(op.inv_a)
comb += do.invert_out.eq(op.inv_out)
comb += do.input_carry.eq(op.cry_in) # carry comes in
comb += do.output_carry.eq(op.cry_out) # carry goes out
# Write a formatted GTKWave "save" file
-def write_gtkw(base_name, top_dut_name, loc):
+def write_gtkw_v1(base_name, top_dut_name, loc):
# hierarchy path, to prepend to signal names
dut = top_dut_name + "."
# color styles
gtkw.trace(dut + "n_ready_i", color=style_input)
+def write_gtkw(gtkw_name, vcd_name, gtkw_dom, gtkw_style=None,
+ module=None, loc=None, color=None, base=None,
+ zoom=-22.9, marker=-1):
+ """ Write a GTKWave document according to the supplied style and DOM.
+
+ :param gtkw_name: name of the generated GTKWave document
+ :param vcd_name: name of the waveform file
+ :param gtkw_dom: DOM style description for the trace pane
+ :param gtkw_style: style for signals, classes and groups
+ :param module: default module
+ :param color: default trace color
+ :param base: default numerical base
+ :param loc: source code location to include as a comment
+ :param zoom: initial zoom level, in GTKWave format
+ :param marker: initial location of a marker
+
+ **gtkw_style format**
+
+ Syntax: ``{selector: {attribute: value, ...}, ...}``
+
+ "selector" can be a signal, class or group
+
+ Signal groups propagate most attributes to their children
+
+ Attribute choices:
+
+ * module: instance path, for prepending to the signal name
+ * color: trace color
+ * base: numerical base for value display
+ * display: alternate text to display in the signal pane
+ * comment: comment to display in the signal pane
+
+ **gtkw_dom format**
+
+ Syntax: ``[signal, (signal, class), (group, [children]), comment, ...]``
+
+ The DOM is a list of nodes.
+
+ Nodes are signals, signal groups or comments.
+
+ * signals are strings, or tuples: ``(signal name, class, class, ...)``
+ * signal groups are tuples: ``(group name, class, class, ..., [nodes])``
+ * comments are: ``{'comment': 'comment string'}``
+
+ In place of a class name, an inline class description can be used.
+ ``(signal, {attribute: value, ...}, ...)``
+ """
+ colors = {
+ 'blue': GTKWColor.blue,
+ 'cycle': GTKWColor.cycle,
+ 'green': GTKWColor.green,
+ 'indigo': GTKWColor.indigo,
+ 'normal': GTKWColor.normal,
+ 'orange': GTKWColor.orange,
+ 'red': GTKWColor.red,
+ 'violet': GTKWColor.violet,
+ 'yellow': GTKWColor.yellow,
+ }
+
+ with open(gtkw_name, "wt") as gtkw_file:
+ gtkw = GTKWSave(gtkw_file)
+ if loc is not None:
+ gtkw.comment("Auto-generated by " + loc)
+ gtkw.dumpfile(vcd_name)
+ # set a reasonable zoom level
+ # also, move the marker to an interesting place
+ gtkw.zoom_markers(zoom, marker)
+
+ # create an empty style, if needed
+ if gtkw_style is None:
+ gtkw_style = dict()
+
+ # create an empty root selector, if needed
+ root_style = gtkw_style.get('', dict())
+
+ # apply styles to the root selector, if provided
+ if module is not None:
+ root_style['module'] = module
+ if color is not None:
+ root_style['color'] = color
+ if base is not None:
+ root_style['base'] = base
+ # base cannot be None, use 'hex' by default
+ if root_style.get('base') is None:
+ root_style['base'] = 'hex'
+
+ # recursively walk the DOM
+ def walk(dom, style):
+ for node in dom:
+ node_name = None
+ children = None
+ # copy the style from the parent
+ node_style = style.copy()
+ # node is a signal name string
+ if isinstance(node, str):
+ node_name = node
+ # apply style from node name, if specified
+ if node_name in gtkw_style:
+ node_style.update(gtkw_style[node_name])
+ # node is a tuple
+ # could be a signal or a group
+ elif isinstance(node, tuple):
+ node_name = node[0]
+ # collect styles from the selectors
+ # order goes from the most specific to most generic
+ # which means earlier selectors override later ones
+ for selector in reversed(node):
+ # update the node style from the selector
+ if isinstance(selector, str):
+ if selector in gtkw_style:
+ node_style.update(gtkw_style[selector])
+ # apply an inline style description
+ elif isinstance(selector, dict):
+ node_style.update(selector)
+ # node is a group if it has a child list
+ if isinstance(node[-1], list):
+ children = node[-1]
+ # comment
+ elif isinstance(node, dict):
+ if 'comment' in node:
+ gtkw.blank(node['comment'])
+ # emit the group delimiters and walk over the child list
+ if children is not None:
+ gtkw.begin_group(node_name)
+ # pass on the group style to its children
+ walk(children, node_style)
+ gtkw.end_group(node_name)
+ # emit a trace, if the node is a signal
+ elif node_name is not None:
+ signal_name = node_name
+ # prepend module name to signal
+ if 'module' in node_style:
+ node_module = node_style['module']
+ if node_module is not None:
+ signal_name = node_module + '.' + signal_name
+ node_color = colors.get(node_style.get('color'))
+ node_base = node_style.get('base')
+ display = node_style.get('display')
+ gtkw.trace(signal_name, color=node_color,
+ datafmt=node_base, alias=display)
+
+ walk(gtkw_dom, root_style)
+
+
def test_shifter():
m = Module()
m.submodules.shf = dut = Shifter(8)
f.write(il)
# Write the GTKWave project file
- write_gtkw("test_shifter", "top.shf", __file__)
+ write_gtkw_v1("test_shifter", "top.shf", __file__)
+
+ # Describe a GTKWave document
+
+ # Style for signals, classes and groups
+ gtkwave_style = {
+ # Root selector. Gives default attributes for every signal.
+ '': {'base': 'dec'},
+ # color the traces, according to class
+ # class names are not hardcoded, they are just strings
+ 'in': {'color': 'orange'},
+ 'out': {'color': 'yellow'},
+ # signals in the debug group have a common color and module path
+ 'debug': {'module': 'top', 'color': 'red'},
+ # display a different string replacing the signal name
+ 'test_case': {'display': 'test case'},
+ }
+
+ # DOM style description for the trace pane
+ gtkwave_desc = [
+ # simple signal, without a class
+ # even so, it inherits the top-level root attributes
+ 'clk',
+ # comment
+ {'comment': 'Shifter Demonstration'},
+ # collapsible signal group
+ ('prev port', [
+ # attach a class style for each signal
+ ('op__sdir', 'in'),
+ ('p_data_i[7:0]', 'in'),
+ ('p_shift_i[7:0]', 'in'),
+ ('p_valid_i', 'in'),
+ ('p_ready_o', 'out'),
+ ]),
+ # Signals in a signal group inherit the group attributes.
+ # In this case, a different module path and color.
+ ('debug', [
+ {'comment': 'Some debug statements'},
+ # inline attributes, instead of a class name
+ ('zero', {'display': 'zero delay shift'}),
+ 'interesting',
+ 'test_case',
+ 'msg',
+ ]),
+ ('internal', [
+ 'fsm_state',
+ 'count[3:0]',
+ 'shift_reg[7:0]',
+ ]),
+ ('next port', [
+ ('n_data_o[7:0]', 'out'),
+ ('n_valid_o', 'out'),
+ ('n_ready_i', 'in'),
+ ]),
+ ]
+
+ write_gtkw("test_shifter_v2.gtkw", "test_shifter.vcd",
+ gtkwave_desc, gtkwave_style,
+ module="top.shf", loc=__file__, marker=10500000)
sim = Simulator(m)
sim.add_clock(1e-6)
class Adder(Elaboratable):
def __init__(self, width):
- self.invert_a = Signal()
+ self.invert_in = Signal()
self.a = Signal(width)
self.b = Signal(width)
self.o = Signal(width, name="add_o")
def elaborate(self, platform):
m = Module()
- with m.If(self.invert_a):
+ with m.If(self.invert_in):
m.d.comb += self.o.eq((~self.a) + self.b)
with m.Else():
m.d.comb += self.o.eq(self.a + self.b)
]
# pass invert (and carry later)
- m.d.comb += add.invert_a.eq(self.op.invert_a)
+ m.d.comb += add.invert_in.eq(self.op.invert_in)
go_now = Signal(reset_less=True) # testing no-delay ALU
yield dut.a.eq(a)
yield dut.b.eq(b)
yield dut.op.insn_type.eq(op)
- yield dut.op.invert_a.eq(inv_a)
+ yield dut.op.invert_in.eq(inv_a)
yield dut.n.ready_i.eq(0)
yield dut.p.valid_i.eq(1)
yield dut.n.ready_i.eq(1)
yield dut.a.eq(0)
yield dut.b.eq(0)
yield dut.op.insn_type.eq(0)
- yield dut.op.invert_a.eq(0)
+ yield dut.op.invert_in.eq(0)
# wait for the ALU to present the output data
while not (yield dut.n.valid_o):
yield dut.a.eq(a)
yield dut.b.eq(b)
yield dut.op.insn_type.eq(op)
- yield dut.op.invert_a.eq(inv_a)
+ yield dut.op.invert_in.eq(inv_a)
yield dut.p.valid_i.eq(1)
yield
# wait for ready_o to be asserted
yield dut.a.eq(0)
yield dut.b.eq(0)
yield dut.op.insn_type.eq(0)
- yield dut.op.invert_a.eq(0)
+ yield dut.op.invert_in.eq(0)
def receive():
# signal readiness to receive data
yield dut.src1_i.eq(a)
yield dut.src2_i.eq(b)
yield dut.oper_i.insn_type.eq(op)
- yield dut.oper_i.invert_a.eq(inv_a)
+ yield dut.oper_i.invert_in.eq(inv_a)
yield dut.oper_i.imm_data.imm.eq(imm)
yield dut.oper_i.imm_data.imm_ok.eq(imm_ok)
yield dut.issue_i.eq(1)
from nmutil.latch import SRLatch, latchregister
from nmutil.byterev import byte_reverse
+from nmutil.extend import exts
from soc.experiment.compalu_multi import go_record, CompUnitRecord
from soc.experiment.l0_cache import PortInterface
p_st_go = Signal(reset_less=True)
sync += p_st_go.eq(self.st.go_i)
+ # decode bits of operand (latched)
+ oper_r = CompLDSTOpSubset(name="oper_r") # Dest register
+ comb += op_is_st.eq(oper_r.insn_type == MicrOp.OP_STORE) # ST
+ comb += op_is_ld.eq(oper_r.insn_type == MicrOp.OP_LOAD) # LD
+ op_is_update = oper_r.ldst_mode == LDSTMode.update # UPDATE
+ op_is_cix = oper_r.ldst_mode == LDSTMode.cix # cache-inhibit
+ comb += self.load_mem_o.eq(op_is_ld & self.go_ad_i)
+ comb += self.stwd_mem_o.eq(op_is_st & self.go_st_i)
+ comb += self.ld_o.eq(op_is_ld)
+ comb += self.st_o.eq(op_is_st)
+
##########################
# FSM implemented through sequence of latches. approximately this:
# - opc_l : opcode
# dest operand latch
comb += wri_l.s.eq(issue_i)
- sync += wri_l.r.eq(reset_w | Repl(self.done_o, self.n_dst))
+ sync += wri_l.r.eq(reset_w | Repl(self.done_o |
+ (self.pi.busy_o & op_is_update),
+ self.n_dst))
# update-mode operand latch (EA written to reg 2)
sync += upd_l.s.eq(reset_i)
comb += rst_l.r.eq(issue_i)
# create a latch/register for the operand
- oper_r = CompLDSTOpSubset(name="oper_r") # Dest register
with m.If(self.issue_i):
sync += oper_r.eq(self.oper_i)
with m.If(self.done_o):
comb += alu_o.eq(src1_or_z + src2_or_imm) # actual EA
m.d.sync += alu_ok.eq(alu_valid) # keep ack in sync with EA
- # decode bits of operand (latched)
- comb += op_is_st.eq(oper_r.insn_type == MicrOp.OP_STORE) # ST
- comb += op_is_ld.eq(oper_r.insn_type == MicrOp.OP_LOAD) # LD
- op_is_update = oper_r.ldst_mode == LDSTMode.update # UPDATE
- op_is_cix = oper_r.ldst_mode == LDSTMode.cix # cache-inhibit
- comb += self.load_mem_o.eq(op_is_ld & self.go_ad_i)
- comb += self.stwd_mem_o.eq(op_is_st & self.go_st_i)
- comb += self.ld_o.eq(op_is_ld)
- comb += self.st_o.eq(op_is_st)
-
############################
# Control Signal calculation
comb += addr_ok.eq(self.pi.addr_ok_o) # no exc, address fine
# byte-reverse on LD
+ revnorev = Signal(64, reset_less=True)
with m.If(oper_r.byte_reverse):
# byte-reverse the data based on ld/st width (turn it to LE)
data_len = oper_r.data_len
lddata_r = byte_reverse(m, 'lddata_r', pi.ld.data, data_len)
- comb += ldd_o.eq(lddata_r) # put reversed- data out
+ comb += revnorev.eq(lddata_r) # put reversed- data out
with m.Else():
- comb += ldd_o.eq(pi.ld.data) # put data out, straight (as BE)
+ comb += revnorev.eq(pi.ld.data) # put data out, straight (as BE)
+
+ # then check sign-extend
+ with m.If(oper_r.sign_extend):
+ comb += ldd_o.eq(exts(revnorev, 32, 64)) # sign-extend
+ with m.Else():
+ comb += ldd_o.eq(revnorev)
+
# ld - ld gets latched in via lod_l
comb += ld_ok.eq(pi.ld.ok) # ld.ok *closes* (freezes) ld data
# assert SET_SIZE_BITS <= TLB_LG_PGSZ
# report "Set indexed by virtual address" severity FAILURE;
assert (LINE_SIZE % ROW_SIZE) == 0 "LINE_SIZE not " \
- "multiple of ROW_SIZE -!- severity FAILURE"
+ "multiple of ROW_SIZE"
- assert (LINE_SIZE % 2) == 0 "LINE_SIZE not power of" \
- "2 -!- severity FAILURE"
+ assert (LINE_SIZE % 2) == 0 "LINE_SIZE not power of 2"
- assert (NUM_LINES % 2) == 0 "NUM_LINES not power of" \
- "2 -!- severity FAILURE"
+ assert (NUM_LINES % 2) == 0 "NUM_LINES not power of 2"
assert (ROW_PER_LINE % 2) == 0 "ROW_PER_LINE not" \
- "power of 2 -!- severity FAILURE"
+ "power of 2"
assert ROW_BITS == (INDEX_BITS + ROW_LINE_BITS) \
- "geometry bits don't add up -!- severity FAILURE"
+ "geometry bits don't add up"
assert (LINE_OFF_BITS = ROW_OFF_BITS + ROW_LINEBITS) \
- "geometry bits don't add up -!- severity FAILURE"
+ "geometry bits don't add up"
assert REAL_ADDR_BITS == (TAG_BITS + INDEX_BITS \
- + LINE_OFF_BITS) "geometry bits don't add up -!-" \
- "severity FAILURE"
+ + LINE_OFF_BITS) "geometry bits don't add up"
assert REAL_ADDR_BITS == (TAG_BITS + ROW_BITS + ROW_OFF_BITS) \
- "geometry bits don't add up -!- severity FAILURE"
+ "geometry bits don't add up"
assert 64 == wishbone_data_bits "Can't yet handle a" \
- "wishbone width that isn't 64-bits -!- severity FAILURE"
+ "wishbone width that isn't 64-bits"
assert SET_SIZE_BITS <= TLB_LG_PGSZ "Set indexed by" \
- "virtual address -!- severity FAILURE"
+ "virtual address"
# -- Latch the request in r0.req as long as we're not stalling
# stage_0 : process(clk)
yield dut.src_i[0].eq(a)
yield dut.src_i[1].eq(b)
yield dut.oper_i.insn_type.eq(op)
- yield dut.oper_i.invert_a.eq(inv_a)
+ yield dut.oper_i.invert_in.eq(inv_a)
yield dut.oper_i.imm_data.imm.eq(imm)
yield dut.oper_i.imm_data.imm_ok.eq(imm_ok)
yield dut.oper_i.zero_a.eq(zero_a)
# at the same time, present the operation
yield self.dut.oper_i.insn_type.eq(self.op)
- yield self.dut.oper_i.invert_a.eq(self.inv_a)
+ yield self.dut.oper_i.invert_in.eq(self.inv_a)
yield self.dut.oper_i.imm_data.imm.eq(self.imm)
yield self.dut.oper_i.imm_data.imm_ok.eq(self.imm_ok)
yield self.dut.oper_i.zero_a.eq(self.zero_a)
# note: rdmaskn must be held, while busy_o is active
# TODO: deactivate rdmaskn when the busy_o cycle ends
yield self.dut.oper_i.insn_type.eq(0)
- yield self.dut.oper_i.invert_a.eq(0)
+ yield self.dut.oper_i.invert_in.eq(0)
yield self.dut.oper_i.imm_data.imm.eq(0)
yield self.dut.oper_i.imm_data.imm_ok.eq(0)
yield self.dut.oper_i.zero_a.eq(0)
#m.d.comb += self..eq(msbaddr)
def set_wr_data(self, m, data, wen):
- m.d.comb += self.ldst.st_data_i.eq(data) # write st to mem
+ m.d.comb += self.ldst.st_data_i.data.eq(data) # write st to mem
m.d.comb += self.ldst.is_st_i.eq(wen) # enable writes
return Const(1, 1) #fixme -- write may be longer than one cycle
('imm_data', Layout((("imm", 64), ("imm_ok", 1)))),
('rc', Layout((("rc", 1), ("rc_ok", 1)))), # Data
('oe', Layout((("oe", 1), ("oe_ok", 1)))), # Data
- ('invert_a', 1),
+ ('invert_in', 1),
('zero_a', 1),
('invert_out', 1),
('write_cr0', 1),
dut_sig = getattr(dut.o.ctx.op, name)
comb += Assert(dut_sig == rec_sig)
- with m.If(rec.invert_a):
+ with m.If(rec.invert_in):
comb += Assert(dut.o.a == ~a)
with m.Else():
comb += Assert(dut.o.a == a)
# as well as carry and overflow generation. This module
# however should not gate the carry or overflow, that's up to the
# output stage
+
+# Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
from nmigen import (Module, Signal, Cat, Repl, Mux, Const)
from nmutil.pipemodbase import PipeModBase
from nmutil.extend import exts
initial_regs[2] = random.randint(0, (1 << 64)-1)
self.add_case(Program(lst, bigendian), initial_regs)
+ def case_addis_nonzero_r0_regression(self):
+ lst = [f"addis 3, 0, 1"]
+ print(lst)
+ initial_regs = [0] * 32
+ initial_regs[0] = 5
+ self.add_case(Program(lst, bigendian), initial_regs)
+
+ def case_addis_nonzero_r0(self):
+ for i in range(10):
+ imm = random.randint(-(1 << 15), (1 << 15)-1)
+ lst = [f"addis 3, 0, {imm}"]
+ print(lst)
+ initial_regs = [0] * 32
+ initial_regs[0] = random.randint(0, (1 << 64)-1)
+ self.add_case(Program(lst, bigendian), initial_regs)
+
def case_rand_imm(self):
insns = ["addi", "addis", "subfic"]
for i in range(10):
dut_sig = getattr(dut.o.ctx.op, name)
comb += Assert(dut_sig == rec_sig)
- with m.If(rec.invert_a):
+ with m.If(rec.invert_in):
comb += Assert(dut.o.a == ~a)
with m.Else():
comb += Assert(dut.o.a == a)
+# License: LGPLv3
+# Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+# Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
+
"""Branch Pipeline
This stage is intended to do most of the work of executing branch
# operand a to be as-is or inverted
a = Signal.like(self.i.a)
- if hasattr(op, "invert_a"):
- with m.If(op.invert_a):
+ op_to_invert = 'ra'
+ if hasattr(self, "invert_op"):
+ op_to_invert = self.invert_op
+
+ if hasattr(op, "invert_in") and op_to_invert == 'ra':
+ with m.If(op.invert_in):
comb += a.eq(~self.i.a)
with m.Else():
comb += a.eq(self.i.a)
comb += self.o.a.eq(a)
+ ##### operand B #####
+
+ # operand b to be as-is or inverted
+ b = Signal.like(self.i.b)
+
+ if hasattr(op, "invert_in") and op_to_invert == 'rb':
+ with m.If(op.invert_in):
+ comb += b.eq(~self.i.b)
+ with m.Else():
+ comb += b.eq(self.i.b)
+ else:
+ comb += b.eq(self.i.b)
+
+ comb += self.o.b.eq(b)
+
##### carry-in #####
# either copy incoming carry or set to 1/0 as defined by op
from soc.fu.div.pipeline import DivBasePipe
from soc.fu.div.pipe_data import DivPipeSpecFSMDivCore
+from soc.fu.div.pipe_data import DivPipeSpecDivPipeCore
from soc.fu.mul.pipeline import MulBasePipe
from soc.fu.mul.pipe_data import MulPipeSpec
fnunit = Function.DIV
def __init__(self, idx):
- super().__init__(DivPipeSpecFSMDivCore, DivBasePipe, idx)
+ #super().__init__(DivPipeSpecFSMDivCore, DivBasePipe, idx)
+ super().__init__(DivPipeSpecDivPipeCore, DivBasePipe, idx)
class MulFunctionUnit(FunctionUnitBaseSingle):
+# License: LGPLv3
+# Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
+# Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+
# This stage is intended to do Condition Register instructions (and ISEL)
# and output, as well as carry and overflow generation.
# NOTE: with the exception of mtcrf and mfcr, we really should be doing
class DivTestCases(TestAccumulatorBase):
+ # modulo
+ def case_modsd_regression(self):
+ lst = [f"modsd 17, 27, 0"]
+ initial_regs = [0] * 32
+ initial_regs[0] = 0xff
+ initial_regs[27] = 0x7fffffffffffffff
+ with Program(lst, bigendian) as prog:
+ self.add_case(prog, initial_regs)
+
+ def case_divduo_regression(self):
+ lst = [f"divduo. 11, 20, 6"]
+ initial_regs = [0] * 32
+ #gpr: 00ff00ff00ff0080 <- r6
+ #gpr: 000000000000007f <- r11
+ #gpr: 7f6e5d4c3b2a1908 <- r20
+ initial_regs[6] = 0x00ff00ff00ff0080
+ initial_regs[20] = 0x7f6e5d4c3b2a1908
+ with Program(lst, bigendian) as prog:
+ self.add_case(prog, initial_regs)
+
def case_0_regression(self):
for i in range(40):
lst = ["divwo 3, 1, 2"]
with Program(lst, bigendian) as prog:
self.add_case(prog, initial_regs)
+ def case_11_regression(self):
+ lst = ["divwo. 3, 1, 2"]
+ initial_regs = [0] * 32
+ initial_regs[1] = 0xffffffffffffffff
+ initial_regs[2] = 0xffffffffffffffff
+ with Program(lst, bigendian) as prog:
+ self.add_case(prog, initial_regs)
+
def case_divw_by_zero_1(self):
lst = ["divw. 3, 1, 2"]
initial_regs = [0] * 32
self.add_case(Program(lst, bigendian), initial_regs,
initial_mem=initial_mem)
+ def case_9_load_algebraic_1(self):
+ lst = ["lwax 3, 4, 2"]
+ initial_regs = [0] * 32
+ initial_regs[1] = 0x5678
+ initial_regs[2] = 0x001c
+ initial_regs[4] = 0x0008
+ initial_mem = {0x0000: (0x5432123412345678, 8),
+ 0x0008: (0xabcdef0187654321, 8),
+ 0x0020: (0xf000000f0000ffff, 8),
+ }
+ self.add_case(Program(lst, bigendian), initial_regs,
+ initial_mem=initial_mem)
+
+ def case_9_load_algebraic_2(self):
+ lst = ["lwax 3, 4, 2"]
+ initial_regs = [0] * 32
+ initial_regs[1] = 0x5678
+ initial_regs[2] = 0x001c
+ initial_regs[4] = 0x0008
+ initial_mem = {0x0000: (0x5432123412345678, 8),
+ 0x0008: (0xabcdef0187654321, 8),
+ 0x0020: (0x7000000f0000ffff, 8),
+ }
+ self.add_case(Program(lst, bigendian), initial_regs,
+ initial_mem=initial_mem)
+
+ def case_9_load_algebraic_3(self):
+ lst = ["lwaux 3, 4, 2"]
+ initial_regs = [0] * 32
+ initial_regs[1] = 0x5678
+ initial_regs[2] = 0x001c
+ initial_regs[4] = 0x0008
+ initial_mem = {0x0000: (0x5432123412345678, 8),
+ 0x0008: (0xabcdef0187654321, 8),
+ 0x0020: (0xf000000f0000ffff, 8),
+ }
+ self.add_case(Program(lst, bigendian), initial_regs,
+ initial_mem=initial_mem)
+
+ def case_9_load_algebraic_4(self):
+ lst = ["lwa 3, 4(4)"]
+ initial_regs = [0] * 32
+ initial_regs[1] = 0x5678
+ initial_regs[4] = 0x0020
+ initial_mem = {0x0000: (0x5432123412345678, 8),
+ 0x0008: (0xabcdef0187654321, 8),
+ 0x0020: (0xf000000f1234ffff, 8),
+ }
+ self.add_case(Program(lst, bigendian), initial_regs,
+ initial_mem=initial_mem)
+
dut_sig = getattr(dut.o.ctx.op, name)
comb += Assert(dut_sig == rec_sig)
- with m.If(rec.invert_a):
+ with m.If(rec.invert_in):
comb += Assert(dut.o.a == ~a)
with m.Else():
comb += Assert(dut.o.a == a)
class LogicalInputStage(CommonInputStage):
def __init__(self, pspec):
super().__init__(pspec, "input")
+ self.invert_op = "rb" # inversion is on register b
def ispec(self):
return LogicalInputData(self.pspec)
return LogicalInputData(self.pspec)
def elaborate(self, platform):
- m = super().elaborate(platform) # covers A-invert, carry, excludes SO
+ m = super().elaborate(platform) # covers B-invert, carry, excludes SO
comb = m.d.comb
ctx = self.i.ctx
- # operand b
- comb += self.o.b.eq(self.i.b)
-
return m
('imm_data', Layout((("imm", 64), ("imm_ok", 1)))),
('rc', Layout((("rc", 1), ("rc_ok", 1)))),
('oe', Layout((("oe", 1), ("oe_ok", 1)))),
- ('invert_a', 1),
+ ('invert_in', 1),
('zero_a', 1),
('input_carry', CryIn),
('invert_out', 1),
# This module however should not gate the carry or overflow, that's up
# to the output stage
+# Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
from nmigen import (Module, Signal, Cat, Repl, Mux, Const, Array)
from nmutil.pipemodbase import PipeModBase
from nmutil.clz import CLZ
class LogicalTestCase(TestAccumulatorBase):
+ def case_complement(self):
+ insns = ["andc", "orc", "nand", "nor"]
+ for i in range(40):
+ choice = random.choice(insns)
+ lst = [f"{choice} 3, 1, 2"]
+ initial_regs = [0] * 32
+ initial_regs[1] = random.randint(0, (1 << 64)-1)
+ initial_regs[2] = random.randint(0, (1 << 64)-1)
+ self.add_case(Program(lst, bigendian), initial_regs)
+
def case_rand(self):
- insns = ["and", "or", "xor"]
+ insns = ["and", "or", "xor", "eqv"]
for i in range(40):
choice = random.choice(insns)
lst = [f"{choice} 3, 1, 2"]
('imm_data', Layout((("imm", 64), ("imm_ok", 1)))),
('rc', Layout((("rc", 1), ("rc_ok", 1)))), # Data
('oe', Layout((("oe", 1), ("oe_ok", 1)))), # Data
- ('invert_a', 1),
- ('zero_a', 1),
- ('invert_out', 1),
('write_cr0', 1),
('is_32bit', 1),
('is_signed', 1),
m = super().elaborate(platform) # handles A, carry and sticky overflow
comb = m.d.comb
- # operands ra and rb
- comb += self.o.rb.eq(self.i.rb)
+ # operand rs
comb += self.o.rs.eq(self.i.rs)
return m
+# License: LGPLv3
+# Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
+# Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+
# This stage is intended to do most of the work of executing shift
# instructions, as well as carry and overflow generation. This module
# however should not gate the carry or overflow, that's up to the
from nmigen import (Elaboratable, Signal, Module)
import math
+# Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
class MaskGen(Elaboratable):
"""MaskGen - create a diff mask
def __init__(self, pspec):
super().__init__(pspec, False)
# convenience
- self.a, self.rs = self.ra, self.rc
+ self.a, self.b, self.rs = self.ra, self.rb, self.rc
+
class ShiftRotPipeSpec(CommonPipeSpec):
from litex.soc.integration.soc_core import SoCCore
from litex.soc.integration.soc_sdram import SoCSDRAM
from litex.soc.integration.builder import Builder
+from litex.soc.integration.common import get_mem_data
from litedram import modules as litedram_modules
from litedram.phy.model import SDRAMPHYModel
class LibreSoCSim(SoCSDRAM):
def __init__(self, cpu="libresoc", debug=False, with_sdram=True,
- #sdram_module = "AS4C16M16",
+ sdram_module = "AS4C16M16",
#sdram_data_width = 16,
- sdram_module = "MT48LC16M16",
+ #sdram_module = "MT48LC16M16",
sdram_data_width = 16,
):
assert cpu in ["libresoc", "microwatt"]
platform = Platform()
sys_clk_freq = int(100e6)
- cpu_data_width = 32
- #cpu_data_width = 64
+ #cpu_data_width = 32
+ cpu_data_width = 64
if cpu_data_width == 32:
variant = "standard32"
else:
variant = "standard"
+ #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
+ # "hello_world/hello_world.bin"
+ ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
+ "tests/1.bin"
+ #ram_fname = None
+
+ ram_init = []
+ if ram_fname:
+ #ram_init = get_mem_data({
+ # ram_fname: "0x00000000",
+ # }, "little")
+ ram_init = get_mem_data(ram_fname, "little")
+
+ # remap the main RAM to reset-start-address
+ self.mem_map["main_ram"] = 0x00000000
+
+ # without sram nothing works, therefore move it to higher up
+ self.mem_map["sram"] = 0x90000000
+
+
# SoCCore -------------------------------------------------------------
SoCSDRAM.__init__(self, platform, clk_freq=sys_clk_freq,
cpu_type = "microwatt",
#bus_data_width = 64,
cpu_variant = variant,
csr_data_width = 32,
- l2_cache_size = 0,
+ l2_size = 0,
uart_name = "sim",
with_sdram = with_sdram,
sdram_module = sdram_module,
sdram_data_width = sdram_data_width,
- integrated_rom_size = 0x10000,
+ integrated_rom_size = 0 if ram_fname else 0x10000,
+ integrated_sram_size = 0x40000,
+ #integrated_main_ram_init = ram_init,
integrated_main_ram_size = 0x00000000 if with_sdram \
else 0x10000000 , # 256MB
- )
+ )
self.platform.name = "sim"
# CRG -----------------------------------------------------------------
self.submodules.crg = CRG(platform.request("sys_clk"))
- ram_init = []
+ #ram_init = []
# SDRAM ----------------------------------------------------
if with_sdram:
self.sync += uptime.eq(uptime + 1)
#self.sync += If(uptime == 1000000000000, Finish())
+ # DMI FSM counter and FSM itself
+ dmicount = Signal(10)
+ dmirunning = Signal(1)
+ dmi_monitor = Signal(1)
dmifsm = FSM()
self.submodules += dmifsm
self.cpu.dmi_req.eq(1), # DMI request
self.cpu.dmi_wr.eq(0), # DMI read
If(self.cpu.dmi_ack,
+ # acknowledge received: capture data.
(NextState("IDLE"),
NextValue(dbg_addr, dmi_addr),
NextValue(dbg_dout, self.cpu.dmi_dout),
NextValue(dbg_msg, 1),
- )
+ ),
),
),
)
)
+ # DMI response received: reset the dmi request and check if
+ # in "monitor" mode
dmifsm.act("IDLE",
- (NextValue(dmi_req, 0),
- NextValue(dmi_addr, 0),
+ If(dmi_monitor,
+ NextState("FIRE_MONITOR"), # fire "monitor" on next cycle
+ ).Else(
+ NextState("START"), # back to start on next cycle
+ ),
+ NextValue(dmi_req, 0),
+ NextValue(dmi_addr, 0),
+ NextValue(dmi_din, 0),
+ NextValue(dmi_wen, 0),
+ )
+
+ # "monitor" mode fires off a STAT request
+ dmifsm.act("FIRE_MONITOR",
+ (NextValue(dmi_req, 1),
+ NextValue(dmi_addr, 1), # DMI STAT address
NextValue(dmi_din, 0),
- NextValue(dmi_wen, 0),
+ NextValue(dmi_wen, 0), # read STAT
NextState("START"), # back to start on next cycle
)
)
If(dbg_addr == 0b10, # PC
pc.eq(dbg_dout), # capture PC
),
- If(dbg_addr == 0b11, # MSR
- Display(" msr: %016x", dbg_dout),
- ),
+ #If(dbg_addr == 0b11, # MSR
+ # Display(" msr: %016x", dbg_dout),
+ #),
If(dbg_addr == 0b101, # GPR
Display(" gpr: %016x", dbg_dout),
),
+ # also check if this is a "stat"
+ If(dbg_addr == 1, # requested a STAT
+ #Display(" stat: %x", dbg_dout),
+ If(dbg_dout & 2, # bit 2 of STAT is "stopped" mode
+ dmirunning.eq(1), # continue running
+ dmi_monitor.eq(0), # and stop monitor mode
+ ),
+ ),
dbg_msg.eq(0)
)
)
)
)
+ self.sync += If(uptime == 4,
+ dmirunning.eq(1),
+ )
+
+ self.sync += If(dmirunning,
+ dmicount.eq(dmicount + 1),
+ )
+
# loop every 1<<N cycles
cyclewid = 9
# get the PC
- self.sync += If(uptime[0:cyclewid] == 4,
+ self.sync += If(dmicount == 4,
(dmi_addr.eq(0b10), # NIA
dmi_req.eq(1),
dmi_wen.eq(0),
)
# kick off a "step"
- self.sync += If(uptime[0:cyclewid] == 8,
+ self.sync += If(dmicount == 8,
(dmi_addr.eq(0), # CTRL
dmi_din.eq(1<<3), # STEP
dmi_req.eq(1),
dmi_wen.eq(1),
+ dmirunning.eq(0), # stop counter, need to fire "monitor"
+ dmi_monitor.eq(1), # start "monitor" instead
)
)
# limit range of pc for debug reporting
- #self.comb += active_dbg.eq((0x5108 <= pc) & (pc <= 0x5234))
+ #self.comb += active_dbg.eq((0x378c <= pc) & (pc <= 0x38d8))
#self.comb += active_dbg.eq((0x0 < pc) & (pc < 0x58))
self.comb += active_dbg.eq(1)
# get the MSR
- self.sync += If(active_dbg & (uptime[0:cyclewid] == 28),
+ self.sync += If(active_dbg & (dmicount == 12),
(dmi_addr.eq(0b11), # MSR
dmi_req.eq(1),
dmi_wen.eq(0),
# read all 32 GPRs
for i in range(32):
- self.sync += If(active_dbg & (uptime[0:cyclewid] == 30+(i*8)),
+ self.sync += If(active_dbg & (dmicount == 14+(i*8)),
(dmi_addr.eq(0b100), # GSPR addr
dmi_din.eq(i), # r1
dmi_req.eq(1),
)
)
- self.sync += If(active_dbg & (uptime[0:cyclewid] == 34+(i*8)),
+ self.sync += If(active_dbg & (dmicount == 18+(i*8)),
(dmi_addr.eq(0b101), # GSPR data
dmi_req.eq(1),
dmi_wen.eq(0),
)
)
+ # monitor bbus read/write
+ self.sync += If(active_dbg & self.cpu.dbus.stb & self.cpu.dbus.ack,
+ Display(" [%06x] dadr: %8x, we %d s %01x w %016x r: %016x",
+ #uptime,
+ 0,
+ self.cpu.dbus.adr,
+ self.cpu.dbus.we,
+ self.cpu.dbus.sel,
+ self.cpu.dbus.dat_w,
+ self.cpu.dbus.dat_r
+ )
+ )
+
+ return
+
# monitor ibus write
self.sync += If(active_dbg & self.cpu.ibus.stb & self.cpu.ibus.ack &
self.cpu.ibus.we,
Display(" [%06x] iadr: %8x, s %01x w %016x",
- uptime,
+ #uptime,
+ 0,
self.cpu.ibus.adr,
self.cpu.ibus.sel,
self.cpu.ibus.dat_w,
self.sync += If(active_dbg & self.cpu.ibus.stb & self.cpu.ibus.ack &
~self.cpu.ibus.we,
Display(" [%06x] iadr: %8x, s %01x r %016x",
- uptime,
+ #uptime,
+ 0,
self.cpu.ibus.adr,
self.cpu.ibus.sel,
self.cpu.ibus.dat_r
)
)
- # monitor bbus read/write
- self.sync += If(active_dbg & self.cpu.dbus.stb & self.cpu.dbus.ack,
- Display(" [%06x] dadr: %8x, we %d s %01x w %016x r: %016x",
- uptime,
- self.cpu.dbus.adr,
- self.cpu.dbus.we,
- self.cpu.dbus.sel,
- self.cpu.dbus.dat_w,
- self.cpu.dbus.dat_r
- )
- )
-
# Build -----------------------------------------------------------------------
def main():
# connect up debug signals
# TODO comb += core.icache_rst_i.eq(dbg.icache_rst_o)
- comb += core.core_stopped_i.eq(dbg.core_stop_o)
comb += dbg.terminate_i.eq(core.core_terminate_o)
comb += dbg.state.pc.eq(pc)
+ #comb += dbg.state.pc.eq(cur_state.pc)
comb += dbg.state.msr.eq(cur_state.msr)
# temporaries
comb += self.state_r_msr.ren.eq(1<<StateRegs.MSR)
m.next = "INSN_READ" # move to "wait for bus" phase
+ with m.Else():
+ comb += core.core_stopped_i.eq(1)
+ comb += dbg.core_stopped_i.eq(1)
# dummy pause to find out why simulation is not keeping up
with m.State("INSN_READ"):
# set to 32 for instruction-memory width=32
imem_reg_wid=64,
# set to 32 to make data wishbone bus 32-bit
- wb_data_wid=32,
+ #wb_data_wid=32,
units=units)
dut = TestIssuer(pspec)
+# License: LPGLv3
+# Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
+# Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+
import tempfile
import subprocess
import struct
+# License: LGPLv3
+# Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
+# Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+
"""POWER Program
takes powerpc assembly instructions and turns them into LE/BE binary
super().__init__(name)
self.test_name = name
+ def test_0_litex_bios_ctr_loop(self):
+ """
+ 32a4: ff ff 63 38 addi r3,r3,-1
+ 32a8: 20 00 63 78 clrldi r3,r3,32
+ 32ac: 01 00 23 39 addi r9,r3,1
+ 32b0: a6 03 29 7d mtctr r9
+ 32b4: 00 00 00 60 nop
+ 32b8: fc ff 00 42 bdnz 32b4 <cdelay+0x10>
+ 32bc: 20 00 80 4e blr
+
+ notes on converting pseudo-assembler to actual:
+
+ * bdnz target (equivalent to: bc 16,0,target)
+ * Clear left immediate clrldi ra,rs,n (n < 64) rldicl ra,rs,0,n
+ * CTR mtctr Rx mtspr 9,Rx
+ """
+ pass
+
def test_0_litex_bios_cmp(self):
"""litex bios cmp test
"""
with Program(lst, bigendian) as program:
self.run_tst_program(program, [12])
+ #@unittest.skip("disable")
+ def test_31_addis(self):
+ """tests for zero not in register zero
+ """
+ lst = [ "rldicr 0, 0, 32, 31",
+ "oris 0, 0, 32767",
+ "ori 0, 0, 65535",
+ "addis 1, 0, 1",
+ "ori 1, 1, 515",
+ "rldicr 1, 1, 32, 31",
+ "oris 1, 1, 1029",
+ "ori 1, 1, 1543",
+ "addis 2, 0, -1",
+ ]
+ with Program(lst, bigendian) as program:
+ self.run_tst_program(program, [0, 1, 2])
+
def run_tst_program(self, prog, initial_regs=None, initial_sprs=None,
initial_mem=None):
initial_regs = [0] * 32