mirror of
https://github.com/m-labs/artiq.git
synced 2024-12-25 11:18:27 +08:00
Merge branch 'wrpll'
This commit is contained in:
commit
bb5ff46f7d
0
artiq/gateware/wrpll/__init__.py
Normal file
0
artiq/gateware/wrpll/__init__.py
Normal file
62
artiq/gateware/wrpll/filters.py
Normal file
62
artiq/gateware/wrpll/filters.py
Normal file
@ -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
|
265
artiq/gateware/wrpll/si549.py
Normal file
265
artiq/gateware/wrpll/si549.py
Normal file
@ -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))
|
||||
]
|
636
artiq/gateware/wrpll/thls.py
Normal file
636
artiq/gateware/wrpll/thls.py
Normal file
@ -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
Block a user