Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MooreToCore] Support ProcedureOp and sequential assign operations #7362

Merged
merged 1 commit into from
Aug 9, 2024

Conversation

maerhart
Copy link
Member

  • Add a first lowering of ProcedureOp and EventOp
  • Add support for blocking and non-blocking assigns
  • Convert RefType to hw::InOutType instead of just the element type

@hailongSun2000 @fabianschuiki Would it make sense to take the moore.ref value as operand for the EventOp directly instead of the read value?

Copy link
Contributor

@fabianschuiki fabianschuiki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks fantastic! 🥳

Regarding ref types in the event op: I think you can theoretically type any expression into an event control statement in SV, so even something like @(posedge ~(a+b)). That might be more annoying to handle at the Moore level than figuring out the a and b to observe at the LLHD level 🤔, WDYT?

We should probably also tweak the moore.event op a bit in the future. Right now you type something like:

moore.event posedge a
moore.event posedge b

to mean "posedge on a or b", but the IR reads more like the process would block and wait for posedge a, and then block and wait for posedge b. I wonder if there's a way to define moore.event such that it lowers to llhd.wait more directly. That way the conversion for moore.procedure will no longer have to create the edge detection, but leave that to the conversion for moore.event instead.

I'm not entirely sure how such an event op would look like to capture things like @(posedge ~(a+b)). Feels like it actually needs a region where you can have arbitrary code to check the condition, since you can have multiple and complicated edge expressions that need to be evaluated as part of the event check. Maybe something like the following?

moore.wait_event {
  %0 = moore.read %a
  %1 = moore.read %b
  %2 = moore.add %0, %1
  %3 = moore.not %2
  %4 = moore.detect_event posedge %3 : -> !moore.event ???
  // maybe others: moore.event_or %4, %5, %6 ???
  moore.yield %4
}

We'd have to handle the more weird cases of event control, like:

always begin
  x = 42;
  @(posedge (a == 0));
  x = 43;
  @(posedge ~(a+b));
  x = 44;
end

Comment on lines +209 to +225
for (auto &operand : operation->getOpOperands()) {
Value value = getSignal(operand.get());
auto memOp = dyn_cast<MemoryEffectOpInterface>(operation);
if (!memOp)
return;

// The process is only sensitive to values that are read.
if (isa<RefType>(operand.get().getType()) &&
memOp.getEffectOnValue<MemoryEffects::Read>(operand.get())
.has_value()) {
if (!alreadyObserved.contains(value))
toObserve.push_back(value);

alreadyObserved.insert(value);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very neat 😎

Comment on lines +193 to +198
signal =
rewriter
.create<UnrealizedConversionCastOp>(loc, convertedType, input)
->getResult(0);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh~! I learned this 😎 !

Comment on lines +254 to +274
if (event.getEdge() == Edge::PosEdge ||
event.getEdge() == Edge::BothEdges) {
Value currVal = rewriter.create<llhd::PrbOp>(loc, signal);
Value trueVal = rewriter.create<hw::ConstantOp>(loc, APInt(1, 1));
Value notOldVal =
rewriter.create<comb::XorOp>(loc, oldValues[i], trueVal);
Value posedge = rewriter.create<comb::AndOp>(loc, notOldVal, currVal);
disjuncts.push_back(posedge);
}
if (event.getEdge() == Edge::NegEdge ||
event.getEdge() == Edge::BothEdges) {
Value currVal = rewriter.create<llhd::PrbOp>(loc, signal);
Value trueVal = rewriter.create<hw::ConstantOp>(loc, APInt(1, 1));
Value notCurrVal =
rewriter.create<comb::XorOp>(loc, currVal, trueVal);
Value posedge =
rewriter.create<comb::AndOp>(loc, oldValues[i], notCurrVal);
disjuncts.push_back(posedge);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing! Here is aimed to monitor the signals/clocks change 🚀.

lib/Conversion/MooreToCore/MooreToCore.cpp Show resolved Hide resolved
@hailongSun2000
Copy link
Member

Could we reimplement moore.wait_event become

moore.procedure always {
  %read_a = moore.read %a
  %read_b = moore.read %b
  %read_c = moore.read %c
  %read_d = moore.read %d
  moore.wait_event (posedege %read_a, negedge %read_b none %read_c, both %read_d){
    x = 42;
    %eq = moore.eq %read_a, %constant_0
    moore.wait_event (posedge %eq){
       // some operations         
    }

    x = 43;
    %not : ~(a + b)
    moore.wait_event (posedge %not){
       // some operations         
    }

    x = 44;
  }
}

When we handle the slang::ast::TimeStatement, we can set point at the wait_event body.
And we can lower wait_event directly into llhd.wait.

pseudo code

llhd.proc{
  cf.br ^bb1
  ^bb1:    // pred: ^bb2, ^bb11
     llhd.wait  (%read_a, %read_b, %read_c, %read_d, ^bb2)
  ^bb2:    // pred: ^bb1
     check
     cf.cond_br %cond ^bb3, ^bb1
  ^bb3:     // pred: ^bb2
     x = 42
     %eq : a == 0
     cf.cond_br %eq ^bb4, ^bb7
  ^bb4:     // pred: ^bb3, ^bb5
     llhd.wait (%eq, ^bb5)
  ^bb5:     // pred: ^bb4
     check
     cf.cond_br %cond0 ^bb6, ^bb4
  ^bb6:     // pred: ^bb5
     // some operation
    cf.br ^bb7
  ^bb7:     // pred: ^bb3, ^bb6
     x = 43;
     %not : ~(a + b)
     cf.cond_br %not ^bb8, ^bb11
  ^bb8:     // pred: ^bb7, ^bb9
      llhd.wait (%not, ^bb9)
  ^bb9:     // pred: ^bb8
      check
      cf.cond_br %cond1 ^bb10, ^bb8
  ^bb10:    // pred: ^bb9
      // some operation
      cf.br ^bb11;
  ^bb11:    // pred: ^bb7, ^bb10
      x = 44;
      cf.br ^bb1;
     
}

If there exist nested event controls, I think it will become more complex.
@fabianschuiki, @maerhart WDYT?

@mingzheTerapines
Copy link
Contributor

mingzheTerapines commented Jul 22, 2024

Awesome, I can continue my initial begin lowering work then.

@fabianschuiki
Copy link
Contributor

moore.procedure always {
  %read_a = moore.read %a
  %read_b = moore.read %b
  %read_c = moore.read %c
  %read_d = moore.read %d
  moore.wait_event (posedege %read_a, negedge %read_b none %read_c, both %read_d){
    x = 42;
    %eq = moore.eq %read_a, %constant_0
    moore.wait_event (posedge %eq){
       // some operations         
    }

    x = 43;
    %not : ~(a + b)
    moore.wait_event (posedge %not){
       // some operations         
    }

    x = 44;
  }
}

The problem with having a moore.read that then feeds into a posedge is that the read will read the current value on e.g. %a before execution reaches the moore.wait_event, which will then block and wait until the posedge happens. However, the value of %read_a is a constant at this point, since the corresponding moore.read is not re-executed. So there will never be any value change observed on %read_a. I think we would want to make wait_event have a region that gets re-executed whenever it tries to check whether one of the edges has happened. So something like:

moore.procedure always {
  moore.wait_event {
    // this gets re-executed to check for posedge events
    %read_a = moore.read %a
    %read_b = moore.read %b
    %read_c = moore.read %c
    %read_d = moore.read %d
    %0 = moore.detect_event posedge %read_a
    %1 = moore.detect_event negedge %read_b
    %2 = moore.detect_event both %read_d
    %3 = moore.event_or %0, %1, %2
    moore.yield %3
  }
  x = 42;
  ...
}

@hailongSun2000
Copy link
Member

You're right! We must read the current value on e.g. re-execute %read_a.

@maerhart maerhart force-pushed the maerhart-moore-proc-lowering branch from ea35f05 to 830173c Compare July 23, 2024 13:21
@maerhart
Copy link
Member Author

maerhart commented Aug 9, 2024

Thanks for the reviews! A lot of great ideas and insights here. We should definitely revisits the design of wait_event. I created an issue linking to this thread so we don't forget about it 🙂

Regarding ref types in the event op: I think you can theoretically type any expression into an event control statement in SV, so even something like @(posedge ~(a+b)). That might be more annoying to handle at the Moore level than figuring out the a and b to observe at the LLHD level 🤔, WDYT?

That's a great point! I agree that doing that at LLHD level probably makes more sense.

@maerhart maerhart force-pushed the maerhart-moore-proc-lowering branch from 830173c to 0de9ffe Compare August 9, 2024 08:37
@maerhart maerhart merged commit 273439e into main Aug 9, 2024
4 checks passed
@maerhart maerhart deleted the maerhart-moore-proc-lowering branch August 9, 2024 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants