diff --git a/magma/sum_type.py b/magma/sum_type.py index 3c3d9de61..69bdfc823 100644 --- a/magma/sum_type.py +++ b/magma/sum_type.py @@ -213,7 +213,10 @@ def __init__(self, T): # Use elsewhen/otherwise to avoid latch detect issues # TODO(leonardt/sum): Enforce that this isn't interleaved with other # when/case statements - if len(self._value.activated_cases) == self._value.num_tags - 1: + if ( + self._value.num_tags > 1 and + len(self._value.activated_cases) == self._value.num_tags - 1 + ): self._when_ctx = otherwise() elif self._value.activated_cases: self._when_ctx = elsewhen(value.check_tag(T)) @@ -245,7 +248,7 @@ def __new__(mcs, name, bases, namespace): tag_len = 0 for key, value in namespace.items(): if isinstance(value, int): - tag_len = max(tag_len, value.bit_length()) + tag_len = max(tag_len, value.bit_length(), 1) if tag_len: tag_map = {} diff --git a/magma/syntax/fsm.py b/magma/syntax/fsm.py index 168d991ee..2ab2c28d1 100644 --- a/magma/syntax/fsm.py +++ b/magma/syntax/fsm.py @@ -69,7 +69,7 @@ def __exit__(self, exc_type, exc_value, exc_tb): self._when_ctx.__exit__(exc_type, exc_value, exc_tb) -def loop(start, end, step=1): +def loop(start, end, step=1, iter_cond=None): with m.no_when(): end_reg = m.Register(type(end))() end_reg.I @= end_reg.O @@ -79,9 +79,12 @@ def loop(start, end, step=1): i.I @= i.O + step i.I @= start m.wait() - # TODO: We explicitly override after wait, otherwise defautl value will set + # TODO: We explicitly override after wait, otherwise default value will set # start, can we avoid this? i.I @= i.O + step + if iter_cond is not None: + with m.when(~iter_cond): + i.I @= i.O when = m.when(end_reg.O < i.O) curr_fsm = _FSM_STACK.peek() when.exit_stack.callback( diff --git a/tests/conftest.py b/tests/conftest.py index e8e8f18cf..2b1115c28 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ from magma.config import config as magma_config +import pytest def pytest_configure(config): @@ -8,3 +9,10 @@ def pytest_configure(config): # # magma_config.use_namer_dict = True magma_config.use_uinspect = True + + +@pytest.fixture +def use_namer_dict(): + magma_config.use_namer_dict = True + yield + magma_config.use_namer_dict = False diff --git a/tests/test_syntax/gold/test_fsm_axi_burst.mlir b/tests/test_syntax/gold/test_fsm_axi_burst.mlir new file mode 100644 index 000000000..8520e9eb1 --- /dev/null +++ b/tests/test_syntax/gold/test_fsm_axi_burst.mlir @@ -0,0 +1,222 @@ +module attributes {circt.loweringOptions = "locationInfoStyle=none,omitVersionComment"} { + hw.module @Memory(in %RADDR: i32, in %CLK: i1, out RDATA: i32) { + %0 = hw.constant 0 : i32 + %1 = hw.constant 0 : i32 + %2 = hw.constant 0 : i1 + %4 = sv.reg name "coreir_mem4294967296x32_inst0" : !hw.inout> + %5 = sv.array_index_inout %4[%RADDR] : !hw.inout>, i32 + %3 = sv.read_inout %5 : !hw.inout + %6 = sv.array_index_inout %4[%0] : !hw.inout>, i32 + sv.alwaysff(posedge %CLK) { + sv.if %2 { + sv.passign %6, %1 : i32 + } + } + hw.output %3 : i32 + } + hw.module @AXIBurstReader(in %ARVALID: i1, in %ARADDR: i32, in %ARSIZE: i3, in %ARLEN: i8, in %RREADY: i1, in %CLK: i1, out ARREADY: i1, out RVALID: i1, out RLAST: i1, out RDATA: i32) { + %1 = hw.struct_create (%0) : !hw.struct + %3 = sv.reg name "Register_inst0" : !hw.inout> + sv.alwaysff(posedge %CLK) { + sv.passign %3, %1 : !hw.struct + } + %5 = hw.constant 0 : i1 + %4 = hw.struct_create (%5) : !hw.struct + sv.initial { + sv.bpassign %3, %4 : !hw.struct + } + %2 = sv.read_inout %3 : !hw.inout> + %6 = hw.struct_extract %2["tag"] : !hw.struct + %7 = hw.constant 0 : i1 + %8 = comb.icmp eq %6, %7 : i1 + %9 = hw.constant 0 : i1 + %12 = sv.reg name "Register_inst1" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %12, %10 : i1 + } + %13 = hw.constant 0 : i1 + sv.initial { + sv.bpassign %12, %13 : i1 + } + %11 = sv.read_inout %12 : !hw.inout + %14 = hw.constant 1 : i1 + %17 = sv.reg name "Register_inst2" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %17, %15 : i1 + } + sv.initial { + sv.bpassign %17, %13 : i1 + } + %16 = sv.read_inout %17 : !hw.inout + %20 = sv.reg name "Register_inst3" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %20, %18 : i8 + } + %21 = hw.constant 0 : i8 + sv.initial { + sv.bpassign %20, %21 : i8 + } + %19 = sv.read_inout %20 : !hw.inout + %24 = sv.reg name "len_reg" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %24, %22 : i8 + } + %25 = hw.constant 0 : i8 + sv.initial { + sv.bpassign %24, %25 : i8 + } + %23 = sv.read_inout %24 : !hw.inout + %28 = sv.reg name "Register_inst4" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %28, %26 : i8 + } + sv.initial { + sv.bpassign %28, %21 : i8 + } + %27 = sv.read_inout %28 : !hw.inout + %29 = hw.constant 1 : i8 + %30 = comb.add %27, %29 : i8 + %31 = hw.constant 0 : i8 + %34 = sv.reg name "Register_inst5" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %34, %32 : i1 + } + sv.initial { + sv.bpassign %34, %13 : i1 + } + %33 = sv.read_inout %34 : !hw.inout + %35 = hw.constant 1 : i8 + %36 = comb.add %27, %35 : i8 + %38 = hw.constant -1 : i1 + %37 = comb.xor %38, %RREADY : i1 + %39 = comb.icmp ult %19, %27 : i8 + %42 = sv.reg name "addr_reg" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %42, %40 : i32 + } + %43 = hw.constant 0 : i32 + sv.initial { + sv.bpassign %42, %43 : i32 + } + %41 = sv.read_inout %42 : !hw.inout + %44 = comb.extract %27 from 0 : (i8) -> i1 + %45 = comb.extract %27 from 1 : (i8) -> i1 + %46 = comb.extract %27 from 2 : (i8) -> i1 + %47 = comb.extract %27 from 3 : (i8) -> i1 + %48 = comb.extract %27 from 4 : (i8) -> i1 + %49 = comb.extract %27 from 5 : (i8) -> i1 + %50 = comb.extract %27 from 6 : (i8) -> i1 + %51 = comb.extract %27 from 7 : (i8) -> i1 + %52 = hw.constant 0 : i1 + %53 = hw.constant 0 : i1 + %54 = hw.constant 0 : i1 + %55 = hw.constant 0 : i1 + %56 = hw.constant 0 : i1 + %57 = hw.constant 0 : i1 + %58 = hw.constant 0 : i1 + %59 = hw.constant 0 : i1 + %60 = hw.constant 0 : i1 + %61 = hw.constant 0 : i1 + %62 = hw.constant 0 : i1 + %63 = hw.constant 0 : i1 + %64 = hw.constant 0 : i1 + %65 = hw.constant 0 : i1 + %66 = hw.constant 0 : i1 + %67 = hw.constant 0 : i1 + %68 = hw.constant 0 : i1 + %69 = hw.constant 0 : i1 + %70 = hw.constant 0 : i1 + %71 = hw.constant 0 : i1 + %72 = hw.constant 0 : i1 + %73 = hw.constant 0 : i1 + %74 = hw.constant 0 : i1 + %75 = hw.constant 0 : i1 + %76 = comb.concat %75, %74, %73, %72, %71, %70, %69, %68, %67, %66, %65, %64, %63, %62, %61, %60, %59, %58, %57, %56, %55, %54, %53, %52, %51, %50, %49, %48, %47, %46, %45, %44 : i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1 + %77 = comb.add %41, %76 : i32 + %78 = hw.constant 1 : i8 + %79 = comb.sub %23, %78 : i8 + %80 = comb.icmp eq %27, %79 : i8 + %81 = hw.constant 0 : i1 + %82 = hw.constant 0 : i32 + %85 = sv.reg name "size_reg" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %85, %83 : i3 + } + %86 = hw.constant 0 : i3 + sv.initial { + sv.bpassign %85, %86 : i3 + } + %84 = sv.read_inout %85 : !hw.inout + %91 = sv.reg : !hw.inout + %10 = sv.read_inout %91 : !hw.inout + %92 = sv.reg : !hw.inout + %40 = sv.read_inout %92 : !hw.inout + %93 = sv.reg : !hw.inout + %83 = sv.read_inout %93 : !hw.inout + %94 = sv.reg : !hw.inout + %22 = sv.read_inout %94 : !hw.inout + %95 = sv.reg : !hw.inout + %87 = sv.read_inout %95 : !hw.inout + %96 = sv.reg : !hw.inout + %15 = sv.read_inout %96 : !hw.inout + %97 = sv.reg : !hw.inout + %18 = sv.read_inout %97 : !hw.inout + %98 = sv.reg : !hw.inout + %26 = sv.read_inout %98 : !hw.inout + %99 = sv.reg : !hw.inout + %32 = sv.read_inout %99 : !hw.inout + %100 = sv.reg : !hw.inout + %88 = sv.read_inout %100 : !hw.inout + %101 = sv.reg : !hw.inout + %89 = sv.read_inout %101 : !hw.inout + %102 = sv.reg : !hw.inout + %90 = sv.read_inout %102 : !hw.inout + %103 = sv.reg : !hw.inout + %0 = sv.read_inout %103 : !hw.inout + sv.alwayscomb { + sv.bpassign %91, %9 : i1 + sv.bpassign %95, %9 : i1 + sv.bpassign %96, %9 : i1 + sv.bpassign %97, %19 : i8 + sv.bpassign %98, %30 : i8 + sv.bpassign %99, %9 : i1 + sv.bpassign %101, %9 : i1 + sv.bpassign %102, %9 : i1 + sv.bpassign %100, %82 : i32 + sv.bpassign %92, %41 : i32 + sv.bpassign %93, %84 : i3 + sv.bpassign %94, %23 : i8 + sv.bpassign %103, %6 : i1 + sv.if %8 { + sv.bpassign %91, %ARVALID : i1 + sv.if %11 { + sv.bpassign %92, %ARADDR : i32 + sv.bpassign %93, %ARSIZE : i3 + sv.bpassign %94, %ARLEN : i8 + sv.bpassign %95, %14 : i1 + sv.bpassign %96, %14 : i1 + sv.if %16 { + sv.bpassign %97, %23 : i8 + sv.bpassign %98, %31 : i8 + sv.bpassign %99, %14 : i1 + sv.if %33 { + sv.bpassign %98, %36 : i8 + sv.if %37 { + sv.bpassign %98, %27 : i8 + } + sv.if %39 { + sv.bpassign %100, %77 : i32 + sv.bpassign %101, %14 : i1 + sv.bpassign %102, %80 : i1 + } else { + sv.bpassign %103, %81 : i1 + } + } + } + } + } + } + %104 = hw.instance "mem" @Memory(RADDR: %88: i32, CLK: %CLK: i1) -> (RDATA: i32) + hw.output %87, %89, %90, %104 : i1, i1, i1, i32 + } +} diff --git a/tests/test_syntax/test_fsm.py b/tests/test_syntax/test_fsm.py index 89b65bee7..f238fe4d1 100644 --- a/tests/test_syntax/test_fsm.py +++ b/tests/test_syntax/test_fsm.py @@ -114,5 +114,64 @@ class Foo(m.Circuit): io.O @= 0xDEED state.next @= State.DONE - m.compile("build/test_fsm_wait_loop", Foo, output="mlir-verilog") + m.compile("build/test_fsm_wait_loop", Foo, output="mlir") assert check_gold(__file__, "test_fsm_wait_loop.mlir") + + +def test_fsm_axi_burst(use_namer_dict): + class Resp(m.Enum): + OKAY = 0b00 + EXOKAY = 0b01 + SLVERR = 0b10 + DECERR = 0b11 + + class Burst(m.Enum): + FIXED = 0b00 + INCR = 0b01 + WRAP = 0b10 + RESERVED = 0b11 + + class State(m.FSMState): + INIT = 0 + + class AXIBurstReader(m.Circuit): + io = m.IO( + ARREADY=m.Out(m.Bit), + + ARVALID=m.In(m.Bit), + ARADDR=m.In(m.Bits[32]), + ARSIZE=m.In(m.Bits[3]), + ARLEN=m.In(m.Bits[8]), + + RREADY=m.In(m.Bit), + + RVALID=m.Out(m.Bit), + RLAST=m.Out(m.Bit), + RDATA=m.Out(m.Bits[32]), + ) + m.ClockIO() + addr_reg = m.Register(m.UInt[32])() + size_reg = m.Register(m.Bits[3])() + len_reg = m.Register(m.UInt[8])() + + mem = m.Memory(2 ** 32, m.Bits[32], read_only=True)() + io.RDATA @= mem.RDATA + + io.ARREADY @= 0 + io.RLAST @= 0 + io.RVALID @= 0 + with m.fsm(State, init=State.INIT) as state: + with m.case(State.INIT): + m.wait_until(io.ARVALID) + addr_reg.I @= io.ARADDR + size_reg.I @= io.ARSIZE + len_reg.I @= io.ARLEN + io.ARREADY @= 1 + m.wait() + with m.loop(0, len_reg.O, iter_cond=io.RREADY) as i: + mem.RADDR @= addr_reg.O + m.zext_to(i, 32) + io.RVALID @= 1 + io.RLAST @= i == (len_reg.O - 1) + state.next @= State.INIT + + m.compile("build/test_fsm_axi_burst", AXIBurstReader, output="mlir") + assert check_gold(__file__, "test_fsm_axi_burst.mlir")