From 3ba817262c7af85acd379ed2aab831473a26907e Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Thu, 20 Aug 2020 14:20:15 +0100 Subject: [PATCH] add a wishbone upconverter --- src/soc/bus/wb_upconvert.py | 118 ++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/soc/bus/wb_upconvert.py diff --git a/src/soc/bus/wb_upconvert.py b/src/soc/bus/wb_upconvert.py new file mode 100644 index 00000000..666e488a --- /dev/null +++ b/src/soc/bus/wb_upconvert.py @@ -0,0 +1,118 @@ +from nmigen import Elaboratable, Module, Signal, Repl, Cat, Mux +from nmigen.utils import log2_int + +class WishboneDownConvert(Elaboratable): + """DownConverter + + This module splits Wishbone accesses from a master interface to a smaller + slave interface. + + Writes: + Writes from master are split N writes to the slave. Access + is acked when the last access is acked by the slave. + + Reads: + Read from master are split in N reads to the the slave. + Read data from the slave are cached before being presented, + concatenated on the last access. + + TODO: + Manage err signal? (Not implemented since we generally don't + use it on Migen/MiSoC modules) + """ + def __init__(self, master, slave): + self.master = master + self.slave = slave + + def elaborate(self, platform): + + master = self.master + slave = self.slave + m = Module() + comb = m.d.comb + sync = m.d.sync + + dw_from = len(master.dat_r) + dw_to = len(slave.dat_w) + ratio = dw_from//dw_to + + # # # + + read = Signal() + write = Signal() + + cached_data = Signal(dw_from) + shift_reg = Signal(dw_from) + + counter = Signal(log2_int(ratio, False)+1) + counter_reset = Signal() + counter_ce = Signal() + with m.If(counter_reset): + sync += counter.eq(0) + with m.Elif(counter_ce): + sync += counter.eq(counter + 1) + + counter_done = Signal() + comb += counter_done.eq(counter == ratio-1) + + # Main FSM + with m.FSM() as fsm: + with m.State("IDLE"): + comb += counter_reset.eq(1) + sync += shift_reg.eq(0) + with m.If(master.stb & master.cyc): + with m.If(master.we): + m.next = "WRITE" + with m.Else(): + m.next = "READ" + + with m.State("WRITE"): + comb += write.eq(1) + comb += slave.we.eq(1) + comb += slave.cyc.eq(1) + with m.If(master.stb & master.cyc): + comb += slave.stb.eq(1) + with m.If(slave.ack): + comb += counter_ce.eq(1) + with m.If(counter_done): + comb += master.ack.eq(1) + m.next = "IDLE" + with m.Elif(~master.cyc): + m.next = "IDLE" + + with m.State("READ"): + comb += read.eq(1) + comb += slave.cyc.eq(1) + with m.If(master.stb & master.cyc): + comb += slave.stb.eq(1) + with m.If(slave.ack): + comb += counter_ce.eq(1) + with m.If(counter_done): + comb += master.ack.eq(1) + comb += master.dat_r.eq(cached_data) + m.next = "IDLE" + with m.Elif(~master.cyc): + m.next = "IDLE" + + # Address + with m.If(counter_done): + comb += slave.cti.eq(7) # indicate end of burst + with m.Else(): + comb += slave.cti.eq(2) + comb += slave.adr.eq(Cat(counter, master.adr)) + + # write Datapath - select fragments of data, depending on "counter" + with m.Switch(counter): + for i in range(ratio): + with m.Case(i): + # select fractions of dat_w and associated "sel" bits + comb += slave.sel.eq(master.sel[i*dw_to//8:(i+1)*dw_to//8]) + comb += slave.dat_w.eq(master.dat_w[i*dw_to:(i+1)*dw_to]) + + # read Datapath - uses cached_data and master.dat_r as a shift-register. + # by the time "counter" is done (counter_done) this is complete + comb += shift_reg.eq(Cat(cached_data[dw_to:], slave.dat_r)) + with m.If(read & counter_ce): + sync += cached_data.eq(shift_reg) + + -- 2.30.2