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

new condtion syntactic suga #1410

Closed
jijingg opened this issue May 9, 2024 · 6 comments
Closed

new condtion syntactic suga #1410

jijingg opened this issue May 9, 2024 · 6 comments

Comments

@jijingg
Copy link
Collaborator

jijingg commented May 9, 2024

@Dolu1990 Can we add another way as an expression for a priority circuit, called cascad

cascad{
  cond(cond0){...}
  cond(cond1){...}
  cond(cond2){...}
  default()    //"default" is necessary in combinational logic but optional in sequential logic.
}

got verilog

  if(cond0) begin
   ...  
  end else if(cond1) begin
   ...  
  end else if(cond2) begin
   ...  
  end else begin
   ...  
  end 

Compared to the when/elsewhen/otherwise approach, it might be more convenient for parameterization. Previously, I had to use multiple whenstatements and their order to achieve priority. The code it generates would also consist of multiple parallel if() statements, prioritized based on the order of 'if()'. For typical Verilog code, this approach doesn't always align well with development habits.

when(cond2){...}
.elsewhen(cond3){...}
.otherwise(...)
when(cond1){...}
when(cond0){...}

got verilog

if(cond2) begin 
... 
end else if(cond3)
...
begin
if(cond1) begin ... end 
if(cond0) begin ... end 

This way, we can parameterize priority circuits similar to how we use 'foreach' inside 'switch()', and generate code in a more intuitive if-else format.

    switch(writeAddress()) {
      for ((address, jobs) <- elementsPerAddress ) address match {
        case address : SingleMapping =>
          assert(address.address % (bus.config.dataWidth/8) == 0)
          is(address.address) {
            doMappedWriteElements(jobs, writeJoinEvent.valid, writeOccur, bus.writeData.data)
          }
        case _ =>
      }
    }

now we can expresion priroty code like this

   cascad{
      for((condtion, express) <- condtionElemeths) {
         cond(condtion){express}
      }
     defualt{xxx}
  }

Of course, the when/elsewhen/otherwise approach also has its place. For certain fixed code structures, this method is more intuitive and aligns better with Verilog writing conventions.

Now we have an additional way to represent priority circuits called 'cascade', rather than replacing the original 'when/elsewhen' approach.

@Dolu1990
Copy link
Member

Dolu1990 commented May 13, 2024

Hi @jijingg,

I think there is something quite close already via the spinal.lib.WhenBuilder :

    import spinal.lib._

    val conds = Bits(8 bits)
    val result = UInt(8 bits)

    val ctx = WhenBuilder()
    ctx.when(conds(0)) {
      result := 0
    }
    ctx.when(conds(1)) {
      result := 1
    }
    if(true){
      ctx.when(conds(2)) {
        result := 2
      }
    }
    ctx.when(conds(3)) {
      result := 3
    }

    for(i <- 5 to 7) ctx.when(conds(i)) {
      result := i
    }

    ctx.otherwise{
      result := 255
    }

=>

  assign when_Utils_l1437 = conds[0];
  always @(*) begin
    if(when_Utils_l1437) begin
      result = 8'h0;
    end else begin
      if(when_Utils_l1440) begin
        result = 8'h01;
      end else begin
        if(when_Utils_l1440_1) begin
          result = 8'h02;
        end else begin
          if(when_Utils_l1440_2) begin
            result = 8'h03;
          end else begin
            if(when_Utils_l1440_3) begin
              result = 8'h05;
            end else begin
              if(when_Utils_l1440_4) begin
                result = 8'h06;
              end else begin
                if(when_Utils_l1440_5) begin
                  result = 8'h07;
                end else begin
                  result = 8'hff;
                end
              end
            end
          end
        end
      end
    end
  end

  assign when_Utils_l1440 = conds[1];
  assign when_Utils_l1440_1 = conds[2];
  assign when_Utils_l1440_2 = conds[3];
  assign when_Utils_l1440_3 = conds[5];
  assign when_Utils_l1440_4 = conds[6];
  assign when_Utils_l1440_5 = conds[7];

More syntax suggars can be added

Would this match the need ?

@jijingg
Copy link
Collaborator Author

jijingg commented May 14, 2024

@Dolu1990 Good, this meets my need for parameterization. It's indeed a feature that's not easy to discover, so it's necessary to add it to the documentation.

@Dolu1990
Copy link
Member

Yes i agree, this is quite underground lacking doc.

@jijingg
Copy link
Collaborator Author

jijingg commented May 15, 2024

Yes i agree, this is quite underground lacking doc.

@Dolu1990 don't worry, I will add it to the document later.

i have another question , Is there a way to move the previous expression to the later , some thing like this ,

  //#PART1
  when(io.addr === 0x0004 && io.wr){
    my_reg := io.wdata
  }.elsewhen(io.clr){
    my_reg  := B(0x1234, 32 bit)
  }
...
...
...
  i know my_reg have many method  like `my_reg.removeStatement(`)
  did there have mothod to get `my_reg.getStatement()` then move to another place
 
 //#PART2
  when(io.set){
    my_reg  := B(0x1234, 32 bit)
  }.elewhen(xxxx){
    my_reg.move_part1_logic_to_here
  }.otherwise{
   my_reg  := B(0x0000, 32 bit)
  }

Of course, this feature is not recommended for ordinary users to use, but it is still necessary to reserve such interfaces for areas that require hacks.

@Dolu1990
Copy link
Member

I will add it to the document later.

Thanks :D
Also feel free to tweek / add syntax sugar on the top

my_reg.move_part1_logic_to_here

So, moving things using my_reg as "anchor" is tricky , because it would not be realy a move, but more a "move statments into a duplicated conditional scope".

I would say that instead, moving the whole when statements would be easier, but there is nothing in place for this yet. But it would just be about patching the linkedlist of statements i think. But there may be some tricky side effects related to statements ordering, i'm not sure.

Otherwise :

def func(){
  when(io.addr === 0x0004 && io.wr){
    my_reg := io.wdata
  }.elsewhen(io.clr){
    my_reg  := B(0x1234, 32 bit)
  }
}
 //#PART2
  when(io.set){
    my_reg  := B(0x1234, 32 bit)
  }.elewhen(xxxx){
    func()
  }.otherwise{
   my_reg  := B(0x0000, 32 bit)
  }

@jijingg
Copy link
Collaborator Author

jijingg commented May 16, 2024

I would say that instead, moving the whole when statements would be easier, but there is nothing in place for this yet. But it would just be about patching the linkedlist of statements i think. But there may be some tricky side effects related to statements ordering, i'm not sure.

Indeed, it might not be easy to handle, and it could be more feasible to choose a def method as a callback. Thanks

@jijingg jijingg closed this as completed May 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants