forked from M-Labs/artiq
Merge branch 'wrpll'
This commit is contained in:
commit
bb5ff46f7d
|
@ -0,0 +1,62 @@
|
||||||
|
helper_xn1 = 0
|
||||||
|
helper_xn2 = 0
|
||||||
|
helper_yn0 = 0
|
||||||
|
helper_yn1 = 0
|
||||||
|
helper_yn2 = 0
|
||||||
|
|
||||||
|
previous_helper_tag = 0
|
||||||
|
|
||||||
|
main_xn1 = 0
|
||||||
|
main_xn2 = 0
|
||||||
|
main_yn0 = 0
|
||||||
|
main_yn1 = 0
|
||||||
|
main_yn2 = 0
|
||||||
|
|
||||||
|
|
||||||
|
def helper(helper_tag):
|
||||||
|
global helper_xn1, helper_xn2, helper_yn0, \
|
||||||
|
helper_yn1, helper_yn2, previous_helper_tag
|
||||||
|
|
||||||
|
helper_xn0 = helper_tag - previous_helper_tag - 32768
|
||||||
|
|
||||||
|
helper_yr = 4294967296
|
||||||
|
|
||||||
|
helper_yn2 = helper_yn1
|
||||||
|
helper_yn1 = helper_yn0
|
||||||
|
helper_yn0 = (
|
||||||
|
((284885689*((217319150*helper_xn0 >> 44) +
|
||||||
|
(-17591968725107*helper_xn1 >> 44))) >> 44) +
|
||||||
|
(-35184372088832*helper_yn1 >> 44) -
|
||||||
|
(17592186044416*helper_yn2 >> 44))
|
||||||
|
|
||||||
|
helper_xn2 = helper_xn1
|
||||||
|
helper_xn1 = helper_xn0
|
||||||
|
|
||||||
|
previous_helper_tag = helper_tag
|
||||||
|
|
||||||
|
helper_yn0 = min(helper_yn0, helper_yr)
|
||||||
|
helper_yn0 = max(helper_yn0, 0 - helper_yr)
|
||||||
|
|
||||||
|
return helper_yn0
|
||||||
|
|
||||||
|
|
||||||
|
def main(main_xn0):
|
||||||
|
global main_xn1, main_xn2, main_yn0, main_yn1, main_yn2
|
||||||
|
|
||||||
|
main_yr = 4294967296
|
||||||
|
|
||||||
|
main_yn2 = main_yn1
|
||||||
|
main_yn1 = main_yn0
|
||||||
|
main_yn0 = (
|
||||||
|
((133450380908*(((35184372088832*main_xn0) >> 44) +
|
||||||
|
((17592186044417*main_xn1) >> 44))) >> 44) +
|
||||||
|
((29455872930889*main_yn1) >> 44) -
|
||||||
|
((12673794781453*main_yn2) >> 44))
|
||||||
|
|
||||||
|
main_xn2 = main_xn1
|
||||||
|
main_xn1 = main_xn0
|
||||||
|
|
||||||
|
main_yn0 = min(main_yn0, main_yr)
|
||||||
|
main_yn0 = max(main_yn0, 0 - main_yr)
|
||||||
|
|
||||||
|
return main_yn0
|
|
@ -0,0 +1,265 @@
|
||||||
|
from migen import *
|
||||||
|
from migen.genlib.fsm import *
|
||||||
|
from migen.genlib.cdc import MultiReg, PulseSynchronizer, BlindTransfer
|
||||||
|
|
||||||
|
from misoc.interconnect.csr import *
|
||||||
|
|
||||||
|
|
||||||
|
class I2CClockGen(Module):
|
||||||
|
def __init__(self, width):
|
||||||
|
self.load = Signal(width)
|
||||||
|
self.clk2x = Signal()
|
||||||
|
|
||||||
|
cnt = Signal.like(self.load)
|
||||||
|
self.comb += [
|
||||||
|
self.clk2x.eq(cnt == 0),
|
||||||
|
]
|
||||||
|
self.sync += [
|
||||||
|
If(self.clk2x,
|
||||||
|
cnt.eq(self.load),
|
||||||
|
).Else(
|
||||||
|
cnt.eq(cnt - 1),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class I2CMasterMachine(Module):
|
||||||
|
def __init__(self, clock_width):
|
||||||
|
self.scl = Signal(reset=1)
|
||||||
|
self.sda_o = Signal(reset=1)
|
||||||
|
self.sda_i = Signal()
|
||||||
|
|
||||||
|
self.submodules.cg = CEInserter()(I2CClockGen(clock_width))
|
||||||
|
self.idle = Signal()
|
||||||
|
self.start = Signal()
|
||||||
|
self.stop = Signal()
|
||||||
|
self.write = Signal()
|
||||||
|
self.read = Signal()
|
||||||
|
self.ack = Signal()
|
||||||
|
self.data = Signal(8)
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
busy = Signal()
|
||||||
|
bits = Signal(4)
|
||||||
|
|
||||||
|
fsm = CEInserter()(FSM("IDLE"))
|
||||||
|
self.submodules += fsm
|
||||||
|
|
||||||
|
fsm.act("IDLE",
|
||||||
|
If(self.start,
|
||||||
|
NextState("START0"),
|
||||||
|
).Elif(self.stop & self.start,
|
||||||
|
NextState("RESTART0"),
|
||||||
|
).Elif(self.stop,
|
||||||
|
NextState("STOP0"),
|
||||||
|
).Elif(self.write,
|
||||||
|
NextValue(bits, 8),
|
||||||
|
NextState("WRITE0"),
|
||||||
|
).Elif(self.read,
|
||||||
|
NextValue(bits, 8),
|
||||||
|
NextState("READ0"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("START0",
|
||||||
|
NextValue(self.scl, 1),
|
||||||
|
NextState("START1"))
|
||||||
|
fsm.act("START1",
|
||||||
|
NextValue(self.sda_o, 0),
|
||||||
|
NextState("IDLE"))
|
||||||
|
|
||||||
|
fsm.act("RESTART0",
|
||||||
|
NextValue(self.scl, 0),
|
||||||
|
NextState("RESTART1"))
|
||||||
|
fsm.act("RESTART1",
|
||||||
|
NextValue(self.sda_o, 1),
|
||||||
|
NextState("START0"))
|
||||||
|
|
||||||
|
fsm.act("STOP0",
|
||||||
|
NextValue(self.scl, 0),
|
||||||
|
NextState("STOP1"))
|
||||||
|
fsm.act("STOP1",
|
||||||
|
NextValue(self.scl, 1),
|
||||||
|
NextValue(self.sda_o, 0),
|
||||||
|
NextState("STOP2"))
|
||||||
|
fsm.act("STOP2",
|
||||||
|
NextValue(self.sda_o, 1),
|
||||||
|
NextState("IDLE"))
|
||||||
|
|
||||||
|
fsm.act("WRITE0",
|
||||||
|
NextValue(self.scl, 0),
|
||||||
|
If(bits == 0,
|
||||||
|
NextValue(self.sda_o, 1),
|
||||||
|
NextState("READACK0"),
|
||||||
|
).Else(
|
||||||
|
NextValue(self.sda_o, self.data[7]),
|
||||||
|
NextState("WRITE1"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fsm.act("WRITE1",
|
||||||
|
NextValue(self.scl, 1),
|
||||||
|
NextValue(self.data[1:], self.data[:-1]),
|
||||||
|
NextValue(bits, bits - 1),
|
||||||
|
NextState("WRITE0"),
|
||||||
|
)
|
||||||
|
fsm.act("READACK0",
|
||||||
|
NextValue(self.scl, 1),
|
||||||
|
NextState("READACK1"),
|
||||||
|
)
|
||||||
|
fsm.act("READACK1",
|
||||||
|
NextValue(self.ack, ~self.sda_i),
|
||||||
|
NextState("IDLE")
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("READ0",
|
||||||
|
NextValue(self.scl, 0),
|
||||||
|
NextState("READ1"),
|
||||||
|
)
|
||||||
|
fsm.act("READ1",
|
||||||
|
NextValue(self.data[0], self.sda_i),
|
||||||
|
NextValue(self.scl, 0),
|
||||||
|
If(bits == 0,
|
||||||
|
NextValue(self.sda_o, ~self.ack),
|
||||||
|
NextState("WRITEACK0"),
|
||||||
|
).Else(
|
||||||
|
NextValue(self.sda_o, 1),
|
||||||
|
NextState("READ2"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fsm.act("READ2",
|
||||||
|
NextValue(self.scl, 1),
|
||||||
|
NextValue(self.data[:-1], self.data[1:]),
|
||||||
|
NextValue(bits, bits - 1),
|
||||||
|
NextState("READ1"),
|
||||||
|
)
|
||||||
|
fsm.act("WRITEACK0",
|
||||||
|
NextValue(self.scl, 1),
|
||||||
|
NextState("IDLE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
run = Signal()
|
||||||
|
self.comb += [
|
||||||
|
run.eq(self.start | self.stop | self.write | self.read),
|
||||||
|
self.idle.eq(~run & fsm.ongoing("IDLE")),
|
||||||
|
self.cg.ce.eq(~self.idle),
|
||||||
|
fsm.ce.eq(run | self.cg.clk2x),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ADPLLProgrammer(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.i2c_divider = Signal(16)
|
||||||
|
self.i2c_address = Signal(7)
|
||||||
|
|
||||||
|
self.adpll = Signal(24)
|
||||||
|
self.stb = Signal()
|
||||||
|
self.busy = Signal()
|
||||||
|
self.nack = Signal()
|
||||||
|
|
||||||
|
self.scl = Signal()
|
||||||
|
self.sda_i = Signal()
|
||||||
|
self.sda_o = Signal()
|
||||||
|
|
||||||
|
self.scl.attr.add("no_retiming")
|
||||||
|
self.sda_o.attr.add("no_retiming")
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
master = I2CMasterMachine(16)
|
||||||
|
self.submodules += master
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
master.cg.load.eq(self.i2c_divider.storage),
|
||||||
|
self.scl.eq(master.scl),
|
||||||
|
master.sda_i.eq(self.sda_i),
|
||||||
|
self.sda_o.eq(master.sda_o)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Si549(Module, AutoCSR):
|
||||||
|
def __init__(self, pads):
|
||||||
|
self.gpio_enable = CSRStorage(reset=1)
|
||||||
|
self.gpio_in = CSRStatus(2)
|
||||||
|
self.gpio_out = CSRStorage(2)
|
||||||
|
self.gpio_oe = CSRStorage(2)
|
||||||
|
|
||||||
|
self.i2c_divider = CSRStorage(16)
|
||||||
|
self.i2c_address = CSRStorage(7)
|
||||||
|
self.errors = CSR(2)
|
||||||
|
|
||||||
|
# in helper clock domain
|
||||||
|
self.adpll = Signal(24)
|
||||||
|
self.adpll_stb = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
programmer = ClockDomainsRenamer("helper")(ADPLLProgrammer())
|
||||||
|
self.submodules += programmer
|
||||||
|
|
||||||
|
self.i2c_divider.storage.attr.add("no_retiming")
|
||||||
|
self.i2c_address.storage.attr.add("no_retiming")
|
||||||
|
self.specials += [
|
||||||
|
MultiReg(self.i2c_divider.storage, programmer.i2c_divider, "helper"),
|
||||||
|
MultiReg(self.i2c_address.storage, programmer.i2c_address, "helper")
|
||||||
|
]
|
||||||
|
self.comb += [
|
||||||
|
programmer.adpll.eq(self.adpll),
|
||||||
|
programmer.adpll_stb.eq(self.adpll_stb)
|
||||||
|
]
|
||||||
|
|
||||||
|
self.gpio_enable.storage.attr.add("no_retiming")
|
||||||
|
self.gpio_out.storage.attr.add("no_retiming")
|
||||||
|
self.gpio_oe.storage.attr.add("no_retiming")
|
||||||
|
|
||||||
|
# SCL GPIO and mux
|
||||||
|
ts_scl = TSTriple(1)
|
||||||
|
self.specials += ts_scl.get_tristate(pads.scl)
|
||||||
|
|
||||||
|
status = Signal()
|
||||||
|
self.comb += self.gpio_in.status[0].eq(status)
|
||||||
|
|
||||||
|
self.specials += MultiReg(ts_scl.i, status)
|
||||||
|
self.comb += [
|
||||||
|
If(self.gpio_enable.storage,
|
||||||
|
ts_scl.o.eq(self.gpio_out.storage[0]),
|
||||||
|
ts_scl.oe.eq(self.gpio_oe.storage[0])
|
||||||
|
).Else(
|
||||||
|
ts_scl.o.eq(programmer.scl),
|
||||||
|
ts_scl.oe.eq(1)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# SDA GPIO and mux
|
||||||
|
ts_sda = TSTriple(1)
|
||||||
|
self.specials += ts_sda.get_tristate(pads.sda)
|
||||||
|
|
||||||
|
status = Signal()
|
||||||
|
self.comb += self.gpio_in.status[1].eq(status)
|
||||||
|
|
||||||
|
self.specials += MultiReg(ts_sda.i, status)
|
||||||
|
self.comb += [
|
||||||
|
If(self.gpio_enable.storage,
|
||||||
|
ts_sda.o.eq(self.gpio_out.storage[1]),
|
||||||
|
ts_sda.oe.eq(self.gpio_oe.storage[1])
|
||||||
|
).Else(
|
||||||
|
ts_sda.o.eq(0),
|
||||||
|
ts_sda.oe.eq(~programmer.sda_o)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
self.specials += MultiReg(ts_sda.i, programmer.sda_i, "helper")
|
||||||
|
|
||||||
|
# Error reporting
|
||||||
|
collision_cdc = BlindTransfer("helper", "sys")
|
||||||
|
self.submodules += collision_cdc
|
||||||
|
self.comb += collision_cdc.i.eq(programmer.stb & programmer.busy)
|
||||||
|
|
||||||
|
nack_cdc = PulseSynchronizer("helper", "sys")
|
||||||
|
self.submodules += nack_cdc
|
||||||
|
self.comb += nack_cdc.i.eq(programmer.nack)
|
||||||
|
|
||||||
|
for n, trig in enumerate([collision_cdc.o, nack_cdc.o]):
|
||||||
|
self.sync += [
|
||||||
|
If(self.errors.re & self.errors.r[n], self.errors.w[n].eq(0)),
|
||||||
|
If(trig, self.errors.w[n].eq(1))
|
||||||
|
]
|
|
@ -0,0 +1,636 @@
|
||||||
|
import inspect
|
||||||
|
import ast
|
||||||
|
from copy import copy
|
||||||
|
import operator
|
||||||
|
from functools import reduce
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from migen import *
|
||||||
|
from migen.genlib.fsm import *
|
||||||
|
|
||||||
|
|
||||||
|
class Isn:
|
||||||
|
def __init__(self, immediate=None, inputs=None, outputs=None):
|
||||||
|
if inputs is None:
|
||||||
|
inputs = []
|
||||||
|
if outputs is None:
|
||||||
|
outputs = []
|
||||||
|
self.immediate = immediate
|
||||||
|
self.inputs = inputs
|
||||||
|
self.outputs = outputs
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
r = "<"
|
||||||
|
r += self.__class__.__name__
|
||||||
|
if self.immediate is not None:
|
||||||
|
r += " (" + str(self.immediate) + ")"
|
||||||
|
for inp in self.inputs:
|
||||||
|
r += " r" + str(inp)
|
||||||
|
if self.outputs:
|
||||||
|
r += " ->"
|
||||||
|
for outp in self.outputs:
|
||||||
|
r += " r" + str(outp)
|
||||||
|
r += ">"
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
class NopIsn(Isn):
|
||||||
|
opcode = 0
|
||||||
|
|
||||||
|
class AddIsn(Isn):
|
||||||
|
opcode = 1
|
||||||
|
|
||||||
|
class SubIsn(Isn):
|
||||||
|
opcode = 2
|
||||||
|
|
||||||
|
class MulShiftIsn(Isn):
|
||||||
|
opcode = 3
|
||||||
|
|
||||||
|
# opcode = 4: MulShift with alternate shift
|
||||||
|
|
||||||
|
class MinIsn(Isn):
|
||||||
|
opcode = 5
|
||||||
|
|
||||||
|
class MaxIsn(Isn):
|
||||||
|
opcode = 6
|
||||||
|
|
||||||
|
class CopyIsn(Isn):
|
||||||
|
opcode = 7
|
||||||
|
|
||||||
|
class InputIsn(Isn):
|
||||||
|
opcode = 8
|
||||||
|
|
||||||
|
class OutputIsn(Isn):
|
||||||
|
opcode = 9
|
||||||
|
|
||||||
|
class EndIsn(Isn):
|
||||||
|
opcode = 10
|
||||||
|
|
||||||
|
|
||||||
|
class ASTCompiler:
|
||||||
|
def __init__(self):
|
||||||
|
self.program = []
|
||||||
|
self.data = []
|
||||||
|
self.next_ssa_reg = -1
|
||||||
|
self.constants = dict()
|
||||||
|
self.names = dict()
|
||||||
|
self.globals = OrderedDict()
|
||||||
|
|
||||||
|
def get_ssa_reg(self):
|
||||||
|
r = self.next_ssa_reg
|
||||||
|
self.next_ssa_reg -= 1
|
||||||
|
return r
|
||||||
|
|
||||||
|
def add_global(self, name):
|
||||||
|
if name not in self.globals:
|
||||||
|
r = len(self.data)
|
||||||
|
self.data.append(0)
|
||||||
|
self.names[name] = r
|
||||||
|
self.globals[name] = r
|
||||||
|
|
||||||
|
def input(self, name):
|
||||||
|
target = self.get_ssa_reg()
|
||||||
|
self.program.append(InputIsn(outputs=[target]))
|
||||||
|
self.names[name] = target
|
||||||
|
|
||||||
|
def emit(self, node):
|
||||||
|
if isinstance(node, ast.BinOp):
|
||||||
|
if isinstance(node.op, ast.RShift):
|
||||||
|
if not isinstance(node.left, ast.BinOp) or not isinstance(node.left.op, ast.Mult):
|
||||||
|
raise NotImplementedError
|
||||||
|
if not isinstance(node.right, ast.Num):
|
||||||
|
raise NotImplementedError
|
||||||
|
left = self.emit(node.left.left)
|
||||||
|
right = self.emit(node.left.right)
|
||||||
|
cons = lambda **kwargs: MulShiftIsn(immediate=node.right.n, **kwargs)
|
||||||
|
else:
|
||||||
|
left = self.emit(node.left)
|
||||||
|
right = self.emit(node.right)
|
||||||
|
if isinstance(node.op, ast.Add):
|
||||||
|
cons = AddIsn
|
||||||
|
elif isinstance(node.op, ast.Sub):
|
||||||
|
cons = SubIsn
|
||||||
|
elif isinstance(node.op, ast.Mult):
|
||||||
|
cons = lambda **kwargs: MulShiftIsn(immediate=0, **kwargs)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
output = self.get_ssa_reg()
|
||||||
|
self.program.append(cons(inputs=[left, right], outputs=[output]))
|
||||||
|
return output
|
||||||
|
elif isinstance(node, ast.Call):
|
||||||
|
if not isinstance(node.func, ast.Name):
|
||||||
|
raise NotImplementedError
|
||||||
|
funcname = node.func.id
|
||||||
|
if node.keywords:
|
||||||
|
raise NotImplementedError
|
||||||
|
inputs = [self.emit(x) for x in node.args]
|
||||||
|
if funcname == "min":
|
||||||
|
cons = MinIsn
|
||||||
|
elif funcname == "max":
|
||||||
|
cons = MaxIsn
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
output = self.get_ssa_reg()
|
||||||
|
self.program.append(cons(inputs=inputs, outputs=[output]))
|
||||||
|
return output
|
||||||
|
elif isinstance(node, (ast.Num, ast.UnaryOp)):
|
||||||
|
if isinstance(node, ast.UnaryOp):
|
||||||
|
if not isinstance(node.operand, ast.Num):
|
||||||
|
raise NotImplementedError
|
||||||
|
if isinstance(node.op, ast.UAdd):
|
||||||
|
transform = lambda x: x
|
||||||
|
elif isinstance(node.op, ast.USub):
|
||||||
|
transform = operator.neg
|
||||||
|
elif isinstance(node.op, ast.Invert):
|
||||||
|
transform = operator.invert
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
node = node.operand
|
||||||
|
else:
|
||||||
|
transform = lambda x: x
|
||||||
|
n = transform(node.n)
|
||||||
|
if n in self.constants:
|
||||||
|
return self.constants[n]
|
||||||
|
else:
|
||||||
|
r = len(self.data)
|
||||||
|
self.data.append(n)
|
||||||
|
self.constants[n] = r
|
||||||
|
return r
|
||||||
|
elif isinstance(node, ast.Name):
|
||||||
|
return self.names[node.id]
|
||||||
|
elif isinstance(node, ast.Assign):
|
||||||
|
output = self.emit(node.value)
|
||||||
|
for target in node.targets:
|
||||||
|
assert isinstance(target, ast.Name)
|
||||||
|
self.names[target.id] = output
|
||||||
|
elif isinstance(node, ast.Return):
|
||||||
|
value = self.emit(node.value)
|
||||||
|
self.program.append(OutputIsn(inputs=[value]))
|
||||||
|
elif isinstance(node, ast.Global):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class Processor:
|
||||||
|
def __init__(self, data_width=32, multiplier_stages=2):
|
||||||
|
self.data_width = data_width
|
||||||
|
self.multiplier_stages = multiplier_stages
|
||||||
|
self.multiplier_shifts = []
|
||||||
|
self.program_rom_size = None
|
||||||
|
self.data_ram_size = None
|
||||||
|
self.opcode_bits = 4
|
||||||
|
self.reg_bits = None
|
||||||
|
|
||||||
|
def get_instruction_latency(self, isn):
|
||||||
|
return {
|
||||||
|
AddIsn: 2,
|
||||||
|
SubIsn: 2,
|
||||||
|
MulShiftIsn: 1 + self.multiplier_stages,
|
||||||
|
MinIsn: 2,
|
||||||
|
MaxIsn: 2,
|
||||||
|
CopyIsn: 1,
|
||||||
|
InputIsn: 1
|
||||||
|
}[isn.__class__]
|
||||||
|
|
||||||
|
def encode_instruction(self, isn, exit):
|
||||||
|
opcode = isn.opcode
|
||||||
|
if isn.immediate is not None and not isinstance(isn, MulShiftIsn):
|
||||||
|
r0 = isn.immediate
|
||||||
|
if len(isn.inputs) >= 1:
|
||||||
|
r1 = isn.inputs[0]
|
||||||
|
else:
|
||||||
|
r1 = 0
|
||||||
|
else:
|
||||||
|
if len(isn.inputs) >= 1:
|
||||||
|
r0 = isn.inputs[0]
|
||||||
|
else:
|
||||||
|
r0 = 0
|
||||||
|
if len(isn.inputs) >= 2:
|
||||||
|
r1 = isn.inputs[1]
|
||||||
|
else:
|
||||||
|
r1 = 0
|
||||||
|
r = 0
|
||||||
|
for value, bits in ((exit, self.reg_bits), (r1, self.reg_bits), (r0, self.reg_bits), (opcode, self.opcode_bits)):
|
||||||
|
r <<= bits
|
||||||
|
r |= value
|
||||||
|
return r
|
||||||
|
|
||||||
|
def instruction_bits(self):
|
||||||
|
return 3*self.reg_bits + self.opcode_bits
|
||||||
|
|
||||||
|
def implement(self, program, data):
|
||||||
|
return ProcessorImpl(self, program, data)
|
||||||
|
|
||||||
|
|
||||||
|
class Scheduler:
|
||||||
|
def __init__(self, processor, reserved_data, program):
|
||||||
|
self.processor = processor
|
||||||
|
self.reserved_data = reserved_data
|
||||||
|
self.used_registers = set(range(self.reserved_data))
|
||||||
|
self.exits = dict()
|
||||||
|
self.program = program
|
||||||
|
self.remaining = copy(program)
|
||||||
|
self.output = []
|
||||||
|
|
||||||
|
def allocate_register(self):
|
||||||
|
r = min(set(range(max(self.used_registers) + 2)) - self.used_registers)
|
||||||
|
self.used_registers.add(r)
|
||||||
|
return r
|
||||||
|
|
||||||
|
def free_register(self, r):
|
||||||
|
assert r >= self.reserved_data
|
||||||
|
self.used_registers.discard(r)
|
||||||
|
|
||||||
|
def find_inputs(self, cycle, isn):
|
||||||
|
mapped_inputs = []
|
||||||
|
for inp in isn.inputs:
|
||||||
|
if inp >= 0:
|
||||||
|
mapped_inputs.append(inp)
|
||||||
|
else:
|
||||||
|
found = False
|
||||||
|
for i in range(cycle):
|
||||||
|
if i in self.exits:
|
||||||
|
r, rm = self.exits[i]
|
||||||
|
if r == inp:
|
||||||
|
mapped_inputs.append(rm)
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
return None
|
||||||
|
return mapped_inputs
|
||||||
|
|
||||||
|
def schedule_one(self, isn):
|
||||||
|
cycle = len(self.output)
|
||||||
|
mapped_inputs = self.find_inputs(cycle, isn)
|
||||||
|
if mapped_inputs is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if isn.outputs:
|
||||||
|
# check that exit slot is free
|
||||||
|
latency = self.processor.get_instruction_latency(isn)
|
||||||
|
exit = cycle + latency
|
||||||
|
if exit in self.exits:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# avoid RAW hazard with global writeback
|
||||||
|
for output in isn.outputs:
|
||||||
|
if output >= 0:
|
||||||
|
for risn in self.remaining:
|
||||||
|
for inp in risn.inputs:
|
||||||
|
if inp == output:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Instruction can be scheduled
|
||||||
|
|
||||||
|
self.remaining.remove(isn)
|
||||||
|
|
||||||
|
for inp, minp in zip(isn.inputs, mapped_inputs):
|
||||||
|
can_free = inp < 0 and all(inp != rinp for risn in self.remaining for rinp in risn.inputs)
|
||||||
|
if can_free:
|
||||||
|
self.free_register(minp)
|
||||||
|
|
||||||
|
if isn.outputs:
|
||||||
|
assert len(isn.outputs) == 1
|
||||||
|
if isn.outputs[0] < 0:
|
||||||
|
output = self.allocate_register()
|
||||||
|
else:
|
||||||
|
output = isn.outputs[0]
|
||||||
|
self.exits[exit] = (isn.outputs[0], output)
|
||||||
|
self.output.append(isn.__class__(immediate=isn.immediate, inputs=mapped_inputs))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def schedule(self):
|
||||||
|
while self.remaining:
|
||||||
|
success = False
|
||||||
|
for isn in self.remaining:
|
||||||
|
if self.schedule_one(isn):
|
||||||
|
success = True
|
||||||
|
break
|
||||||
|
if not success:
|
||||||
|
self.output.append(NopIsn())
|
||||||
|
self.output += [NopIsn()]*(max(self.exits.keys()) - len(self.output) + 1)
|
||||||
|
return self.output
|
||||||
|
|
||||||
|
|
||||||
|
class CompiledProgram:
|
||||||
|
def __init__(self, processor, program, exits, data, glbs):
|
||||||
|
self.processor = processor
|
||||||
|
self.program = program
|
||||||
|
self.exits = exits
|
||||||
|
self.data = data
|
||||||
|
self.globals = glbs
|
||||||
|
|
||||||
|
def pretty_print(self):
|
||||||
|
for cycle, isn in enumerate(self.program):
|
||||||
|
l = "{:4d} {:15}".format(cycle, str(isn))
|
||||||
|
if cycle in self.exits:
|
||||||
|
l += " -> r{}".format(self.exits[cycle])
|
||||||
|
print(l)
|
||||||
|
|
||||||
|
def dimension_processor(self):
|
||||||
|
self.processor.program_rom_size = len(self.program)
|
||||||
|
self.processor.data_ram_size = len(self.data)
|
||||||
|
self.processor.reg_bits = (self.processor.data_ram_size - 1).bit_length()
|
||||||
|
for isn in self.program:
|
||||||
|
if isinstance(isn, MulShiftIsn) and isn.immediate not in self.processor.multiplier_shifts:
|
||||||
|
self.processor.multiplier_shifts.append(isn.immediate)
|
||||||
|
|
||||||
|
def encode(self):
|
||||||
|
r = []
|
||||||
|
for i, isn in enumerate(self.program):
|
||||||
|
exit = self.exits.get(i, 0)
|
||||||
|
r.append(self.processor.encode_instruction(isn, exit))
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def compile(processor, function):
|
||||||
|
node = ast.parse(inspect.getsource(function))
|
||||||
|
assert isinstance(node, ast.Module)
|
||||||
|
assert len(node.body) == 1
|
||||||
|
node = node.body[0]
|
||||||
|
assert isinstance(node, ast.FunctionDef)
|
||||||
|
assert len(node.args.args) == 1
|
||||||
|
arg = node.args.args[0].arg
|
||||||
|
body = node.body
|
||||||
|
|
||||||
|
astcompiler = ASTCompiler()
|
||||||
|
for node in body:
|
||||||
|
if isinstance(node, ast.Global):
|
||||||
|
for name in node.names:
|
||||||
|
astcompiler.add_global(name)
|
||||||
|
arg_r = astcompiler.input(arg)
|
||||||
|
for node in body:
|
||||||
|
astcompiler.emit(node)
|
||||||
|
if isinstance(node, ast.Return):
|
||||||
|
break
|
||||||
|
for glbl, location in astcompiler.globals.items():
|
||||||
|
new_location = astcompiler.names[glbl]
|
||||||
|
if new_location != location:
|
||||||
|
astcompiler.program.append(CopyIsn(inputs=[new_location], outputs=[location]))
|
||||||
|
|
||||||
|
scheduler = Scheduler(processor, len(astcompiler.data), astcompiler.program)
|
||||||
|
scheduler.schedule()
|
||||||
|
|
||||||
|
program = copy(scheduler.output)
|
||||||
|
program.append(EndIsn())
|
||||||
|
|
||||||
|
max_reg = max(max(max(isn.inputs + [0]) for isn in program), max(v[1] for k, v in scheduler.exits.items()))
|
||||||
|
|
||||||
|
return CompiledProgram(
|
||||||
|
processor=processor,
|
||||||
|
program=program,
|
||||||
|
exits={k: v[1] for k, v in scheduler.exits.items()},
|
||||||
|
data=astcompiler.data + [0]*(max_reg - len(astcompiler.data) + 1),
|
||||||
|
glbs=astcompiler.globals)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseUnit(Module):
|
||||||
|
def __init__(self, data_width):
|
||||||
|
self.stb_i = Signal()
|
||||||
|
self.i0 = Signal((data_width, True))
|
||||||
|
self.i1 = Signal((data_width, True))
|
||||||
|
self.stb_o = Signal()
|
||||||
|
self.o = Signal((data_width, True))
|
||||||
|
|
||||||
|
|
||||||
|
class NopUnit(BaseUnit):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class OpUnit(BaseUnit):
|
||||||
|
def __init__(self, op, data_width, stages):
|
||||||
|
BaseUnit.__init__(self, data_width)
|
||||||
|
|
||||||
|
o = op(self.i0, self.i1)
|
||||||
|
stb_o = self.stb_i
|
||||||
|
for i in range(stages):
|
||||||
|
n_o = Signal(data_width)
|
||||||
|
n_stb_o = Signal()
|
||||||
|
self.sync += [
|
||||||
|
n_o.eq(o),
|
||||||
|
n_stb_o.eq(stb_o)
|
||||||
|
]
|
||||||
|
o = n_o
|
||||||
|
stb_o = n_stb_o
|
||||||
|
self.comb += [
|
||||||
|
self.o.eq(o),
|
||||||
|
self.stb_o.eq(stb_o)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SelectUnit(BaseUnit):
|
||||||
|
def __init__(self, op, data_width):
|
||||||
|
BaseUnit.__init__(self, data_width)
|
||||||
|
|
||||||
|
self.sync += [
|
||||||
|
self.stb_o.eq(self.stb_i),
|
||||||
|
If(op(self.i0, self.i1),
|
||||||
|
self.o.eq(self.i0)
|
||||||
|
).Else(
|
||||||
|
self.o.eq(self.i1)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CopyUnit(BaseUnit):
|
||||||
|
def __init__(self, data_width):
|
||||||
|
BaseUnit.__init__(self, data_width)
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
self.stb_o.eq(self.stb_i),
|
||||||
|
self.o.eq(self.i0)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class InputUnit(BaseUnit):
|
||||||
|
def __init__(self, data_width, input_stb, input):
|
||||||
|
BaseUnit.__init__(self, data_width)
|
||||||
|
self.buffer = Signal(data_width)
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
self.stb_o.eq(self.stb_i),
|
||||||
|
self.o.eq(self.buffer)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OutputUnit(BaseUnit):
|
||||||
|
def __init__(self, data_width, output_stb, output):
|
||||||
|
BaseUnit.__init__(self, data_width)
|
||||||
|
|
||||||
|
self.sync += [
|
||||||
|
output_stb.eq(self.stb_i),
|
||||||
|
output.eq(self.i0)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessorImpl(Module):
|
||||||
|
def __init__(self, pd, program, data):
|
||||||
|
self.input_stb = Signal()
|
||||||
|
self.input = Signal((pd.data_width, True))
|
||||||
|
|
||||||
|
self.output_stb = Signal()
|
||||||
|
self.output = Signal((pd.data_width, True))
|
||||||
|
|
||||||
|
self.busy = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
program_mem = Memory(pd.instruction_bits(), pd.program_rom_size, init=program)
|
||||||
|
data_mem0 = Memory(pd.data_width, pd.data_ram_size, init=data)
|
||||||
|
data_mem1 = Memory(pd.data_width, pd.data_ram_size, init=data)
|
||||||
|
self.specials += program_mem, data_mem0, data_mem1
|
||||||
|
|
||||||
|
pc = Signal(pd.instruction_bits())
|
||||||
|
pc_next = Signal.like(pc)
|
||||||
|
pc_en = Signal()
|
||||||
|
self.sync += pc.eq(pc_next)
|
||||||
|
self.comb += [
|
||||||
|
If(pc_en,
|
||||||
|
pc_next.eq(pc + 1)
|
||||||
|
).Else(
|
||||||
|
pc_next.eq(0)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
program_mem_port = program_mem.get_port()
|
||||||
|
self.specials += program_mem_port
|
||||||
|
self.comb += program_mem_port.adr.eq(pc_next)
|
||||||
|
|
||||||
|
s = 0
|
||||||
|
opcode = Signal(pd.opcode_bits)
|
||||||
|
self.comb += opcode.eq(program_mem_port.dat_r[s:s+pd.opcode_bits])
|
||||||
|
s += pd.opcode_bits
|
||||||
|
r0 = Signal(pd.reg_bits)
|
||||||
|
self.comb += r0.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
|
||||||
|
s += pd.reg_bits
|
||||||
|
r1 = Signal(pd.reg_bits)
|
||||||
|
self.comb += r1.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
|
||||||
|
s += pd.reg_bits
|
||||||
|
exit = Signal(pd.reg_bits)
|
||||||
|
self.comb += exit.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
|
||||||
|
|
||||||
|
data_read_port0 = data_mem0.get_port()
|
||||||
|
data_read_port1 = data_mem1.get_port()
|
||||||
|
self.specials += data_read_port0, data_read_port1
|
||||||
|
self.comb += [
|
||||||
|
data_read_port0.adr.eq(r0),
|
||||||
|
data_read_port1.adr.eq(r1)
|
||||||
|
]
|
||||||
|
|
||||||
|
data_write_port = data_mem0.get_port(write_capable=True)
|
||||||
|
data_write_port_dup = data_mem1.get_port(write_capable=True)
|
||||||
|
self.specials += data_write_port, data_write_port_dup
|
||||||
|
self.comb += [
|
||||||
|
data_write_port_dup.we.eq(data_write_port.we),
|
||||||
|
data_write_port_dup.adr.eq(data_write_port.adr),
|
||||||
|
data_write_port_dup.dat_w.eq(data_write_port.dat_w),
|
||||||
|
data_write_port.adr.eq(exit)
|
||||||
|
]
|
||||||
|
|
||||||
|
nop = NopUnit(pd.data_width)
|
||||||
|
adder = OpUnit(operator.add, pd.data_width, 1)
|
||||||
|
subtractor = OpUnit(operator.sub, pd.data_width, 1)
|
||||||
|
if pd.multiplier_shifts:
|
||||||
|
if len(pd.multiplier_shifts) != 1:
|
||||||
|
raise NotImplementedError
|
||||||
|
multiplier = OpUnit(lambda a, b: a * b >> pd.multiplier_shifts[0],
|
||||||
|
pd.data_width, pd.multiplier_stages)
|
||||||
|
else:
|
||||||
|
multiplier = NopUnit(pd.data_width)
|
||||||
|
minu = SelectUnit(operator.lt, pd.data_width)
|
||||||
|
maxu = SelectUnit(operator.gt, pd.data_width)
|
||||||
|
copier = CopyUnit(pd.data_width)
|
||||||
|
inu = InputUnit(pd.data_width, self.input_stb, self.input)
|
||||||
|
outu = OutputUnit(pd.data_width, self.output_stb, self.output)
|
||||||
|
units = [nop, adder, subtractor, multiplier, minu, maxu, copier, inu, outu]
|
||||||
|
self.submodules += units
|
||||||
|
|
||||||
|
for unit in units:
|
||||||
|
self.sync += unit.stb_i.eq(0)
|
||||||
|
self.comb += [
|
||||||
|
unit.i0.eq(data_read_port0.dat_r),
|
||||||
|
unit.i1.eq(data_read_port1.dat_r),
|
||||||
|
If(unit.stb_o,
|
||||||
|
data_write_port.we.eq(1),
|
||||||
|
data_write_port.dat_w.eq(unit.o)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
decode_table = [
|
||||||
|
(NopIsn.opcode, nop),
|
||||||
|
(AddIsn.opcode, adder),
|
||||||
|
(SubIsn.opcode, subtractor),
|
||||||
|
(MulShiftIsn.opcode, multiplier),
|
||||||
|
(MulShiftIsn.opcode + 1, multiplier),
|
||||||
|
(MinIsn.opcode, minu),
|
||||||
|
(MaxIsn.opcode, maxu),
|
||||||
|
(CopyIsn.opcode, copier),
|
||||||
|
(InputIsn.opcode, inu),
|
||||||
|
(OutputIsn.opcode, outu)
|
||||||
|
]
|
||||||
|
for allocated_opcode, unit in decode_table:
|
||||||
|
self.sync += If(pc_en & (opcode == allocated_opcode), unit.stb_i.eq(1))
|
||||||
|
|
||||||
|
fsm = FSM()
|
||||||
|
self.submodules += fsm
|
||||||
|
fsm.act("IDLE",
|
||||||
|
pc_en.eq(0),
|
||||||
|
NextValue(inu.buffer, self.input),
|
||||||
|
If(self.input_stb, NextState("PROCESSING"))
|
||||||
|
)
|
||||||
|
fsm.act("PROCESSING",
|
||||||
|
self.busy.eq(1),
|
||||||
|
pc_en.eq(1),
|
||||||
|
If(opcode == EndIsn.opcode,
|
||||||
|
pc_en.eq(0),
|
||||||
|
NextState("IDLE")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
a = 0
|
||||||
|
b = 0
|
||||||
|
c = 0
|
||||||
|
|
||||||
|
def foo(x):
|
||||||
|
global a, b, c
|
||||||
|
c = b
|
||||||
|
b = a
|
||||||
|
a = x
|
||||||
|
return 4748*a + 259*b - 155*c
|
||||||
|
|
||||||
|
|
||||||
|
def simple_test(x):
|
||||||
|
global a
|
||||||
|
a = a + (x*4 >> 1)
|
||||||
|
return a
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
proc = Processor()
|
||||||
|
cp = compile(proc, simple_test)
|
||||||
|
cp.pretty_print()
|
||||||
|
cp.dimension_processor()
|
||||||
|
print(cp.encode())
|
||||||
|
proc_impl = proc.implement(cp.encode(), cp.data)
|
||||||
|
|
||||||
|
def send_values(values):
|
||||||
|
for value in values:
|
||||||
|
yield proc_impl.input.eq(value)
|
||||||
|
yield proc_impl.input_stb.eq(1)
|
||||||
|
yield
|
||||||
|
yield proc_impl.input.eq(0)
|
||||||
|
yield proc_impl.input_stb.eq(0)
|
||||||
|
yield
|
||||||
|
while (yield proc_impl.busy):
|
||||||
|
yield
|
||||||
|
@passive
|
||||||
|
def receive_values(callback):
|
||||||
|
while True:
|
||||||
|
while not (yield proc_impl.output_stb):
|
||||||
|
yield
|
||||||
|
callback((yield proc_impl.output))
|
||||||
|
yield
|
||||||
|
|
||||||
|
run_simulation(proc_impl, [send_values([42, 40, 10, 10]), receive_values(print)])
|
Loading…
Reference in New Issue