diff --git a/cores/litex.nix b/cores/litex.nix new file mode 100644 index 0000000..1f14921 --- /dev/null +++ b/cores/litex.nix @@ -0,0 +1,216 @@ +{ stdenv, fetchgit, python3Packages, nmigen }: + +python3Packages.buildPythonPackage { + name = "litex"; + + src = fetchgit { + url = "git://github.com/enjoy-digital/litex.git"; + rev = "33d7cc5fc81cc7785be217f64d756db6092aeff6"; + sha256 = "1qpbkn9440175qm5myaalv3nr2sq4fhpz6dicgjv62j907w05wnv"; + fetchSubmodules = true; + }; + + patchPhase = + '' + # those won't be supported + rm -rf litex/gen + rm -rf litex/build + rm -rf litex/boards + rm test/test_targets.py + + # may be supported later + rm -rf litex/soc/integration + + # FIXME: this spews out a lot of irrelevant "pattern doesn't match anything" warnings + for file in `find test litex -name "*.py"`; do + substituteInPlace "$file" \ + --replace "from migen.util.misc import xdir" "from litex.compat import xdir" \ + --replace "from migen.genlib.roundrobin import *" "from litex.roundrobin import *" \ + --replace "from migen.genlib import roundrobin" "from litex import roundrobin" \ + --replace "migen.genlib.misc" "litex.misc" \ + --replace "migen.fhdl.tracer" "litex.compat" \ + --replace migen nmigen.compat + done + + # BufferizeEndpoints is actually not used anywhere in LiteX, but breaks due to no ModuleTransformer + substituteInPlace litex/soc/interconnect/stream.py --replace ModuleTransformer object + # this import is not required + substituteInPlace litex/soc/interconnect/stream_packet.py --replace "from litex.gen import *" "" + # we do not need the alternative simulator + substituteInPlace test/test_axi.py --replace "from litex.gen.sim import *" "" + + cat > litex/compat.py << EOF + from nmigen.compat import * + from nmigen.tracer import get_var_name + + def xdir(obj, return_values=False): + for attr in dir(obj): + if attr[:2] != "__" and attr[-2:] != "__": + if return_values: + yield attr, getattr(obj, attr) + else: + yield attr + + def get_obj_var_name(name): + if name is None: + return get_var_name() + else: + return name + EOF + + cat > litex/roundrobin.py << EOF + from nmigen.compat import * + + (SP_WITHDRAW, SP_CE) = range(2) + + class RoundRobin(Module): + def __init__(self, n, switch_policy=SP_WITHDRAW): + self.request = Signal(n) + self.grant = Signal(max=max(2, n)) + self.switch_policy = switch_policy + if self.switch_policy == SP_CE: + self.ce = Signal() + + ### + + if n > 1: + cases = {} + for i in range(n): + switch = [] + for j in reversed(range(i+1, i+n)): + t = j % n + switch = [ + If(self.request[t], + self.grant.eq(t) + ).Else( + *switch + ) + ] + if self.switch_policy == SP_WITHDRAW: + case = [If(~self.request[i], *switch)] + else: + case = switch + cases[i] = case + statement = Case(self.grant, cases) + if self.switch_policy == SP_CE: + statement = If(self.ce, statement) + self.sync += statement + else: + self.comb += self.grant.eq(0) + EOF + + cat > litex/misc.py << EOF + from nmigen.compat import * + + def split(v, *counts): + r = [] + offset = 0 + for n in counts: + if n != 0: + r.append(v[offset:offset+n]) + else: + r.append(None) + offset += n + return tuple(r) + + + def displacer(signal, shift, output, n=None, reverse=False): + if shift is None: + return output.eq(signal) + if n is None: + n = 2**len(shift) + w = len(signal) + if reverse: + r = reversed(range(n)) + else: + r = range(n) + l = [Replicate(shift == i, w) & signal for i in r] + return output.eq(Cat(*l)) + + + def chooser(signal, shift, output, n=None, reverse=False): + if shift is None: + return output.eq(signal) + if n is None: + n = 2**len(shift) + w = len(output) + cases = {} + for i in range(n): + if reverse: + s = n - i - 1 + else: + s = i + cases[i] = [output.eq(signal[s*w:(s+1)*w])] + return Case(shift, cases).makedefault() + + + def timeline(trigger, events): + lastevent = max([e[0] for e in events]) + counter = Signal(max=lastevent+1) + + counterlogic = If(counter != 0, + counter.eq(counter + 1) + ).Elif(trigger, + counter.eq(1) + ) + # insert counter reset if it doesn't naturally overflow + # (test if lastevent+1 is a power of 2) + if (lastevent & (lastevent + 1)) != 0: + counterlogic = If(counter == lastevent, + counter.eq(0) + ).Else( + counterlogic + ) + + def get_cond(e): + if e[0] == 0: + return trigger & (counter == 0) + else: + return counter == e[0] + sync = [If(get_cond(e), *e[1]) for e in events] + sync.append(counterlogic) + return sync + + + class WaitTimer(Module): + def __init__(self, t): + self.wait = Signal() + self.done = Signal() + + # # # + + count = Signal(bits_for(t), reset=t) + self.comb += self.done.eq(count == 0) + self.sync += \ + If(self.wait, + If(~self.done, count.eq(count - 1)) + ).Else(count.eq(count.reset)) + + + class BitSlip(Module): + def __init__(self, dw): + self.i = Signal(dw) + self.o = Signal(dw) + self.value = Signal(max=dw) + + # # # + + r = Signal(2*dw) + self.sync += r.eq(Cat(r[dw:], self.i)) + cases = {} + for i in range(dw): + cases[i] = self.o.eq(r[i:dw+i]) + self.sync += Case(self.value, cases) + + EOF + ''; + + propagatedBuildInputs = [ python3Packages.pyserial nmigen ]; + + meta = with stdenv.lib; { + description = "Partial LiteX cores and tools (via nMigen compatibility mode)"; + homepage = "http://enjoy-digital.fr"; + license = licenses.bsd2; + maintainers = [ maintainers.sb0 ]; + }; +} diff --git a/default.nix b/default.nix index 7bcda84..263d633 100644 --- a/default.nix +++ b/default.nix @@ -14,6 +14,7 @@ rec { name = "vexriscv-small"; scalaToRun = "vexriscv.demo.GenSmallAndProductive"; }; + litex = pkgs.callPackage ./cores/litex.nix { inherit nmigen; }; heavycomps = pkgs.callPackage ./heavycomps.nix { inherit nmigen; };