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

Question: Using AUTOs to help build complex state machines #591

Closed
veripoolbot opened this issue Dec 18, 2012 · 3 comments
Closed

Question: Using AUTOs to help build complex state machines #591

veripoolbot opened this issue Dec 18, 2012 · 3 comments
Labels

Comments

@veripoolbot
Copy link
Collaborator


Author Name: Leith Johnson
Original Redmine Message: 973 from https://www.veripool.org


I have a need to build some algorithmically complex state machines. This is certainly doable with Verilog and a text editor, but it can be tedious (veritedium??) to manage and maintain the state assignments.

Within the structure of the Verilog language I would like to create the notion of a program counter and symbolic branching. The basic structure is a case statement. A case label represents a line in the program.

Minimum clock cycle is not a strong requirement. Manipulating state assignments to improve performance shouldn't be required.

Accomplishling what I need is basically a two step process. First step is to scan the code, assigning values to the case labels and building a symbol table. Step 2 is scan the code again, replacing the symbols with values from the symbol table.

I'm already using AUTOs. Perhaps some enhancement so AUTOs can do what I need.

Create two new AUTOs:

/AUTO_PC(optional_symbol)/ : begin
end

pc_next_state = /AUTO_SYM(symbol)/;

Upon AUTOs evaluation, these turn in to something like:

/AUTO_PC(optional_symbol)/ 12'h001 : begin
end

pc_next_state = /AUTO_SYM(symbol)/ 12'h030;

One issue is the width/format of the state assignments. One solution might be to have AUTOs count the AUTO_PCs it encounters and do a clogb2 to come up with the pc width. This would require another pass. Another solution is to default pc width to 32 bits. A small amount of manipulation of the register assignment should provide synthesis with enough clues to eliminate the unnecessary bits, while keeping lint at bay.

I'm not attached to the idea of using AUTOs to solve this problem. In fact the best answer might be if somebody knows of a general purpose microcode assembler?? Or maybe some combination of preprocessing and AUTOs?? I'm open to any suggestion.

I've looked a bit at verilog-mode.el. My last lisp experience was in college many years ago... and there's a lot more there than I expected.

BTW, one of the really cool aspects of this is the micromachine "instructions" are limited only by what can be written in synthesizable Verilog. In my case an alternative is synthesizing a small general purpose processor. But then I'll have to create some interface layer to talk to what I'm controlling. Furthermore, talking to what I'm controlling will be through some indirect register interface. If I can get this to work out, I'll talk directly to what I'm controlling in a manner consistent with the functionality. And if I need some new instruction down the road, I'll just make it up on the fly.

Below is what it might look like without any automated symbol assignment. This little micromachine resets, waits for while after reset, then sets the output pulse_something high for 1 state out of three forever. With the way I did it, I'm trying to illustrate how maintenance can be difficult. As shown, inserting a new state between PC_Q and PC_B would require reworking all the state assignment following PC_Q.

  localparam PC_WIDTH = 8;
  localparam ONE = 1;
  localparam TWO = 2;
  localparam THREE = 3;
  localparam FOUR = 4;

  localparam [PC_WIDTH-1:0] PC_RESET = ONE[PC_WIDTH-1:0];
  localparam [PC_WIDTH-1:0] PC_A = TWO[PC_WIDTH-1:0];
  localparam [PC_WIDTH-1:0] PC_Q = THREE[PC_WIDTH-1:0];
  localparam [PC_WIDTH-1:0] PC_B = FOUR[PC_WIDTH-1:0];  

  input clk;
  input reset;

  output pulse_something;

  reg [PC_WIDTH-1:0] pc, pc_next_state;
  reg [7:0] rst_wait, rst_wait_next_state;

  always @(*) begin
     
 // RESET next states    
     if (reset == 1'b1) begin    
       pc_next_state = PC_RESET;
       rst_wait_next_state = 8'hff;
       pulse_something = 1'b0;
     end else begin

// Default next states.
       pc_next_state = pc + ONE[PC_WIDTH-1:0];
       rst_wait_next_state = rst_wait - 8'h01;
       pulse_something = 1'b0;

// PC based actions and next states. 
       case (pc)
         PC_RESET: begin
           if (rst_wait != 8'h00) pc_next_state = PC_RESET;
         end
         
         PC_A: begin
         end
         
         PC_Q: begin
           pulse_something = 1'b1;
         end
         
         PC_B: begin
           pc_next_state = PC_A;
         end
         
       end // else: !if(reset == 1'b1)
     endcase // case (pc)
  end // always @ (*)

  always @(posedge clk) pc <= pc_next_state;
  always @(posedge clk) rst_wait <= rst_wait_next_state;

Thanks in advance for any suggestions.

Leith Johnson

@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Wilson Snyder (@wsnyder)
Original Date: 2012-12-20T21:34:31Z


I'd be inclined to just have an AUTOINSERTLISP call perl and write the generator in perl. Likely to be a lot less verbose. But as you say there's a lot of possible ways to do it.

@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Leith Johnson
Original Date: 2013-01-03T19:30:04Z


OK, I've managed to hack together an alpha version
of "verilog-autolabel".

Its an addendum to verilog-autos. It uses the verilog-auto-hook
and verilog-auto-delete-auto-hook so that its invoked along with
the regular autos.

Here's an example before AUTOs:

  always @(*) begin

 // RESET next states    
     if (reset == 1'b1) begin    
       pc_next_state = /*AUTOLINK("PC_RESET")*/;
       rst_wait_next_state = 8'hff;
       pulse_something = 1'b0;
     end else begin

// Default next states.
       pc_next_state = pc + ONE[PC_WIDTH-1:0];
       rst_wait_next_state = rst_wait - 8'h01;
       pulse_something = 1'b0;

// PC based actions and next states. 
       case (pc)
         /*AL("PC_RESET")*/: begin
           if (rst_wait != 8'h00) pc_next_state = /*AK("pc_3")*/;
         end

         /*AUTOLABEL("pc_0")*/:begin
           pulse_something = 1'b0;
         end

         /*AUTOLABEL*/:begin
           pulse_something = 1'b1;
         end

         /*AL*/:begin
           pc_next_state = /*AK("pc_3")*/;
         end

	/*AL("pc_3", "10")*/:begin
           pc_next_state = /*AK("pc_3")*/;
         end
       endcase // case (pc)
     end // else: !if(reset == 1'b1)
    end // always @ (*)

And after AUTOs:

  always @(*) begin

 // RESET next states    
     if (reset == 1'b1) begin    
       pc_next_state = /*AUTOLINK("PC_RESET")*/16'd0;
       rst_wait_next_state = 8'hff;
       pulse_something = 1'b0;
     end else begin

// Default next states.
       pc_next_state = pc + ONE[PC_WIDTH-1:0];
       rst_wait_next_state = rst_wait - 8'h01;
       pulse_something = 1'b0;

// PC based actions and next states. 
       case (pc)
         /*AL("PC_RESET")*/16'd0: begin
           if (rst_wait != 8'h00) pc_next_state = /*AK("pc_3")*/16'd10;
         end

         /*AUTOLABEL("pc_0")*/16'd1:begin
           pulse_something = 1'b0;
         end

         /*AUTOLABEL*/16'd2:begin
           pulse_something = 1'b1;
         end

         /*AL*/16'd3:begin
           pc_next_state = /*AK("pc_3")*/16'd10;
         end

	/*AL("pc_3", "10")*/16'd10:begin
           pc_next_state = /*AK("pc_3")*/16'd10;
         end
       endcase // case (pc)
     end // else: !if(reset == 1'b1)
    end // always @ (*)


Attached find a zip file. Its got a README. Installation instructions
can be found at the top of the verilog-autolabel.el file.

Enjoy.

Leith

@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Wilson Snyder (@wsnyder)
Original Date: 2013-01-04T00:14:58Z


Thanks for contributing.

Good job. Code makes sense and ended up as less code than I would have suspected.

BTW, I think if you use autolabel with an "address" 5, then one with "address 4" then one without an address you'll stomp over 5 again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant