6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Wed Sep 25, 2024 5:27 pm

All times are UTC




Post new topic Reply to topic  [ 62 posts ]  Go to page Previous  1, 2, 3, 4, 5  Next
Author Message
PostPosted: Fri Dec 07, 2012 11:19 pm 
Offline

Joined: Mon Mar 02, 2009 7:27 pm
Posts: 3258
Location: NC, USA
I just bought 2 books as cheap as I could find them used on amazon.com:

1)"Designing Digital Computer Systems with Verilog", Lilja, David J.
2)"Verilog Digital Computer Design: Algorithms Into Hardware", Arnold, Mark

_________________
65Org16:https://github.com/ElEctric-EyE/verilog-6502


Top
 Profile  
Reply with quote  
PostPosted: Sat Dec 08, 2012 12:13 am 
Offline
User avatar

Joined: Mon Apr 23, 2012 12:28 am
Posts: 760
Location: Huntsville, AL
I have not purchased these myself.

You seem to be picking up Verilog quite well. You, Arlet, and White Flame are moving along real well with your VGA FPGA board. Arlet's coding style is somthing to emulate, and White Flame seems to have strong grasp of graphics principles. Meaning that real design and debug you are doing now is going to be a better teacher than most books on HDL-based computer design and development. I generally look to them for syntax references, general concepts, and for some coding principles that can keep me out of trouble.

BTW, I really have enjoyed following your work on line drawing, and have learned a few things about Bresenham's that I wasn't aware of before.

_________________
Michael A.


Top
 Profile  
Reply with quote  
PostPosted: Sat Dec 08, 2012 7:51 am 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
I agree with MichaelM. Actually sitting down and designing something is the best way to learn.


Top
 Profile  
Reply with quote  
PostPosted: Sat Dec 08, 2012 5:10 pm 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
Youtube also features some interesting training videos, such as this one about Spartan 6 clocking resources


Top
 Profile  
Reply with quote  
PostPosted: Sat Dec 08, 2012 5:17 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
Arlet wrote:
I agree with MichaelM. Actually sitting down and designing something is the best way to learn.

I agree, but I'd really like to see a one-page crib sheet: not something to explain why things are as they are, merely setting out some syntactic patterns:
- how to code a state machine using don't care values
- when to use =, and when <=
- when to use wire, and when reg
- how to declare and use a module
- how to sign-extend a bus value

I haven't yet found such a sheet!

Edit: MichaelM posted a short list of "rules" here: viewtopic.php?p=23321#p23321
Edit: But there are small clashes with the "rules" here: viewtopic.php?p=23313#p23313

Cheers
Ed


Last edited by BigEd on Tue May 26, 2015 8:15 pm, edited 2 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Sat Dec 08, 2012 8:19 pm 
Offline
User avatar

Joined: Mon Apr 23, 2012 12:28 am
Posts: 760
Location: Huntsville, AL
BigEd:

I'll give it a go.
Quote:
- how to code a state machine using don't care values

State machines with don't care values are generally risky propositions. I don't recommend using don't care values, but if you carefully construct your state and transition signal list, then you can make use of the casex() endcase construct in a manner that should keep you out of trouble. (Please note that some Verilog/SystemVerilog experts have a few choice thoughts regarding the use of casex() endcase constructs. I use them sparingly, and I have no intention of building ASICs from my RTL.) I have posted a design on GitHUB that demonstrates how a state machine using don't cares can be implemented. Please note that the technique shown in that example uses a LUT-based ROM to implement all of the state transition equations. That is, it's another example of a microprogrammed state machine, but one that does not use microprogram controller like the M65C02 core or a loadable counter. Instead it uses a simple next state control field and a priority encoder built using a casex() endcase construct. (The state machine current state register and priority encoder start at line 424.) The implementation uses the priority encoder to service 6 requests (held in FFs until acknowledged/serviced by the SM) on a priority basis. The 6 requests are RTF - Reset Transmit FIFO, WR_TF - Write Transmit FIFO, RD_TF - Read Transmit FIFO, RRF - Reset Receive FIFO, WR_RF - Write Receive FIFO, and RD_RF - Read Receive FIFO. The header describes the assumptions made and defines the reasons for the prioritization implemented in the priority encoder that determines the next state from the inputs and the next state field in the ROM.
Quote:
- when to use =, and when <=

Their behavior is defined for simulation, i.e. hardware modelling, instead of synthesis. From a synthesis perspective, the blocking assignment, '=', is only used within assign statements, and the non-blocking assignment, '<=', is only used within always @() blocks. From a simulation perspective, the blocking assignment forces the simulator to sequentially execute the statements in the order they appear in the source. Again from a simulation perspective, the non-blocking assignment allows the simulator to evaluate the statement in a non-sequential manner.

In simulation, blocking and non-blocking assignments may be mixed as needed to model the desired behavior. From a synthesis perspective, the time sequence simulation concepts represented by blocking and non-blocking assignments have no real meaning. Thus, the restrictions stated above regarding the use of the blocking (assign statements) and non-blocking (always blocks) in synthesizable RTL code is intended to make the synthesis results match the simulation results.

From the tenor of your questions it appears to me that you have the some reservations regarding writing synthesizable RTL and getting the results that you expect. Let me say that is natural. I went through that phase many years ago after I decided that I would henceforth perform FPGA work in HDL. Please be aware that I had supported several FPGA projects in HDL through the early adoption phases, and was not fan of the results. Thus, our internal work was performed using schematics and CoreGen until the later releases of ISE 8.2i. Prior to that, FPGA Express, Synplicity, etc. were tools we used to support customer driven project implementation requirements, but the results were always less satisfactory and the amount of effort our customers expended to get the desired behaviour did not convince me of the efficacy of the approach or the toolsets.

From ISE 8.2i onward, I have been working exclusively in Verilog. I have transitioned several customers from schematic-based designs to HDL-based designs. The key to these transitions has been to code all Verilog code in a synthesizable manner using a consistent style, and developing a testbench for each module. The test benches do not have to be particularly sophisticated, but they do need to generate a waveform that you can examine and satisfy yourself that the diagrams match your expectations for the behaviour you are trying to achieve.

I believe that the last statement is critical for your transition to HDL-based design. If you don't convince yourself that your basic HDL coding style produces the results you expect, then you will not have any confidence in the FPGA configuration image. I have tried to internalize the concepts that Ken Chapman discusses regarding how the synthesizer extracts the FF definition from the RTL source. In this way, the results I see in the simulator reinforces my confidence that my coding style generates FPGA code that will operate in the HW in the same manner that I see it operate on the ISim output screen. Armed with that confidence it is much easier to debug the HW since the inner workings of the FPGA are not a major concern.
Quote:
- when to use wire, and when reg

Use a wire whenever the assignments are made in an assign statement, and use reg whenever the assignment is made in an always block.

I will digress here a bit. The Verilog concepts of wire and reg are generally compatible with an electronics engineer's concepts for those two terms. However, let me restate something that I've written before in this thread: a reg does not mean FF in Verilog. A reg in Verilog means that the variable can hold a value. This concept is a simulation concept, and only represents an indirect connection to a FF in hardware.

From a synthesis perspective, i.e. generating real HW, a Verilog reg variable will be required in order to generate a FF. But Verilog will generate a bi-stable latch for any reg variable, combinatorial or otherwise, in the event an incomplete if or case endcase statement is used within an always block to define what would normally be a combinatorial signal.
Quote:
- how to declare and use a module

The best way to declare a module within the ISE design environment is to use the menus. Under the Project menu, the New Source, Add source, or Add Copy of Source sub-menus provide the needed dialogs. The Project=>New Source sub-menu will bring up a dialog that let's you add a Verilog module or testbench to the project. Once that is selected, another dialog will take you through the process of declaring the input, output, and inout ports of the module.

There does appear to be several gotchas, at least in ISE 10.1i, regarding modules and the module port declarations. First, each module requires a module <module_name> #(<parameter declarations>)(<port declarations>); declaration which is terminated by an endmodule statement like case() is terminated by endcase. The gotcha is that the endmodule statement must be followed by a blank line.

Another gotcha that gets me every once in a while, is that the parameters and the port declarations are separated by commas. Every once in a while I find myself adding parameters or ports, and instead of separating them from the others with commas, I mistakenly separate them with semicolons. This is a big no-no. When you save your changes, the parser performs some checks, and when it finds these syntax errors, the files are generally removed from the project source list. They are not gone from the project, but they are no longer considered source files. The tool simply moved them into another list: the user documents list under the project directory at the top of the soruces list. I end up chasing my tail for a few minutes, until I realize that the project directory now shows the "lost" file(s). You can open these files with any editor, and correct these easy to find syntax errors, save the files, and they will be restored into the normal project source files list.

Now to use the module inside another module, i.e. instantiate the module. You must identify the top most module yourself. This is done by selecting the file from the project sources list, right clicking, and selecting the Set as Top Module option.

The port list of the top-most module are the I/O pins of the FPGA. They should be assigned using a User Constraints File (UCF) to specific I/O pins. However, the highest performance is generally achieved if the tools are used to set the pin assignments of the pins. That being said, I've never had the luxury of designing an FPGA board where the FPGA design has been completed, and the pinout determined by the tools themselves in order to achieve the best performance. Invariably, I've had to release boards with FPGAs for fabrications long before the FPGA design is complete. One thing that I recommend here is to define your clocking requirements early, and let the tool select the pins for the clocks.

All other modules will be instantianted under the top module. ISE provides a tool for generating the instantiation template for any module. Once the module implementation and testing is complete, and with the Sources for Implementation option selected, select the module in the project sources list. In the Processes for window, expand the Design Utilities function. Under that function, there are two options listed for the module: (1) Creating a schematic symbol, and (2) Viewing the instantiation template. Double clicking on the Viewing the instantiation template will pop up a window which will contain the source for the instantiation template. Select the template, and paste it into whatever module you want to instantiate the module. Edit the connections, and assign an instance name. The module should now appear at the correct hierarchy level in your design.
Quote:
- how to sign-extend a bus value

I generally use the vector replication construct to perform this action. Vector replication is defined in a nested manner using curly braces. Vector replication has the following syntax:
Code:
{count{replication_vector}}

As an example, assume a 5 bit vector with a sign bit in its most significant bit is to be sign extended to 16 bits. Also assume, that the 5-bit vector and the 16-bit vector are both declared a wire variables. (Example applies equally to variables declared as reg.)
Code:
wire   [4:0] in;
wire   [15:0] out;

assign out = {{11{in[4]}}, in};

One comment about vector replication is that the replication vector contained in the inner most curly braces, i.e. in[4], is not restricted to single bits. The following is an example which creates an alternating pattern of 1s and 0s over a 32-bit output vector using a 2-bit input vector:
Code:
wire   [1:0] in = 2'b10;
wire   [31:0] out;

assign out = {16{in}};


Hope this helps.

_________________
Michael A.


Top
 Profile  
Reply with quote  
PostPosted: Sat Dec 08, 2012 8:35 pm 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
MichaelM wrote:
In simulation, blocking and non-blocking assignments may be mixed as needed to model the desired behavior. From a synthesis perspective, the time sequence simulation concepts represented by blocking and non-blocking assignments have no real meaning. Thus, the restrictions stated above regarding the use of the blocking (assign statements) and non-blocking (always blocks) in synthesizable RTL code is intended to make the synthesis results match the simulation results.

A good use of '=' in synthesis is something like this:
Code:
a = expression
if( test )
   a = a + 1;

This is equivalent to the following:
Code:
if( test )
   a = expression + 1;
else
   a = expression;

Using the '=' makes the code simpler. I often use this technique in my state machines:
Code:
always @*
    next = state;
    if( foo )
        next = BAR;

The first 'next = state' acts as a default to stay in the same state when none of the following conditions match.


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 09, 2012 9:12 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
Thanks Michael, Arlet.
(That thing with the mistaken semicolons sounds very unhelpful!)
I had a quick look at Arlet's cpu.v, and the guideline applied there seems to be:
- for always @* [combinational block], use =
- for always @(posedge clk) [flops], use <=
I'd like to say that we always = into a wire or output pin, and always <= into a reg, but that seems not to be true.
(I'm looking for simply stated guidelines which don't explain and may not be necessary but are sufficient to be coding safely. One would say, for example, that all assignments to a given signal must be made within the same block, and that all paths through that block must assign to the signal.)
Cheers
Ed


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 09, 2012 10:18 pm 
Offline
User avatar

Joined: Mon Apr 23, 2012 12:28 am
Posts: 760
Location: Huntsville, AL
I myself learned something from the exchange. Namely, that it possible to use blocking assignments within always blocks in synthesis. I had resolved in my mind that it was not possible to use blocking assignments in always blocks during synthesis, but I took Arlet's example and constructed an example for myself. The following code with blocking assignments in an always blocks fails synthesis:
Code:
module Top_Blocking(
    input   Rst,
    input   Clk,
    input   In1,
    input   In2,
    output  Out
);

always @(*)
begin
    if(Rst)
        Out = 0;
    else
        Out = ((In1) ? In2 : 1);
end

endmodule

The synthesizer reports:
Quote:
Compiling verilog file "Top_Blocking.v" in library work
Module <Top_Blocking> compiled
ERROR:HDLCompilers:247 - "Top_Blocking.v" line 33 Reference to scalar wire 'Out' is not a legal reg or variable lvalue
ERROR:HDLCompilers:44 - "Top_Blocking.v" line 33 Illegal left hand side of blocking assignment
ERROR:HDLCompilers:247 - "Top_Blocking.v" line 35 Reference to scalar wire 'Out' is not a legal reg or variable lvalue
ERROR:HDLCompilers:44 - "Top_Blocking.v" line 35 Illegal left hand side of blocking assignment
Analysis of file <"Top_Blocking.prj"> failed.

This report is the reason why I assumed that blocking assignments are not allowed during synthesis in always blocks. The issue is easily resolved by changing the type of Out from a wire to a reg.
Code:
module Top_Blocking(
    input   Rst,
    input   Clk,
    input   In1,
    input   In2,
    output  reg Out
);

always @(*)
begin
    if(Rst)
        Out = 0;
    else
        Out = ((In1) ? In2 : 1);
end

endmodule

Thus, my previous statement that the target of an assignment of an always block is a reg is correct. In either of these two examples, Out is a combinatorial output. A simple change to the sensitivity list of the always block changes Out from a combinatorial signal into a registered signal, while using a blocking assignment within a synthesized always block and clearly demonstrating Arlet's point.
Code:
module Top_Blocking(
    input   Rst,
    input   Clk,
    input   In1,
    input   In2,
    output  reg Out
);

always @(posedge Clk)
begin
    if(Rst)
        Out = 0;
    else
        Out = ((In1) ? In2 : 1);
end

endmodule

It is not possible to mix the use of blocking and non-blocking assignments in an always block.
Code:
module Top_Blocking(
    input   Rst,
    input   Clk,
    input   In1,
    input   In2,
    output  reg Out
);

always @(posedge Clk)
begin
    if(Rst)
        Out = 0;
    else
        Out <= ((In1) ? In2 : 1);
end

endmodule

Which results in the following error message from the synthesizer.
Quote:
Analyzing top module <Top_Blocking>.
ERROR:Xst:880 - "Top_Blocking.v" line 35: Cannot mix blocking and non blocking assignments on signal <Out>.

Found 1 error(s). Aborting synthesis.

Thus, the simple rules that you are trying to get to are:
Quote:
target of an assignment in an always block must be a reg
target of an assignment in an assign statement must be a wire
use blocking (=) or non-blocking assignment (<=) to a reg in an always block
use blocking assignment (=) to wire in an assign statement

_________________
Michael A.


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 10, 2012 1:23 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
Thanks Michael.

It seems a helpful guideline to me to distinguish
- always @ clocked blocks, which should be simple flop updates, handling reset and clear, maybe primitive operations like addition or increment
- always @* combinatorial blocks, for clouds of related logic
although I'm not sure how to state that. One might also perhaps have a guideline about when to use a combinatorial always block and when to use an assign statement - on the grounds that having more than one way to do something leaves the novice unclear and likely to write code in an internally inconsistent style.

As a matter of interest, there's a well-known paper by Clifford E Cummings about race conditions in Verilog, which Trevor Williams used as guidance for some optional checking in his coverage analysis tool 'covered' - in the docs we find:
    - All sequential logic and latches use non-blocking assignments.
    - All combinational logic in an always block uses blocking assignments.
    - All mixed sequential and combinational logic in the same always block uses non-blocking assignments.
    - Blocking and non-blocking assignments should not be used in the same always block.
    - Assignments made to a variable should only be done within one always block.
    - The $strobe system call should only be used to display variables that were assigned using non-blocking assignments. (Not currently implemented)
    - No #0 procedural assignments should exist. (Not currently implemented)

Cheers
Ed


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 10, 2012 2:43 am 
Offline
User avatar

Joined: Mon Apr 23, 2012 12:28 am
Posts: 760
Location: Huntsville, AL
There's a subtlety that's missing from these papers. I defer to Mr. Cummings in most cases, but there is one subtle topic where the so called experts leave it to the student to discover the implications and ramifications. I am not a fan of this approach.

The subtlety missed most often is that reg is a Verilog variable that can hold a value. From the previous statement, I want to highlight two concepts: (1) the Verilog data types which are used for synthesis are wire and reg; and (2) Verilog variables are just program variables in a large SW application and not actual wires and FFs in hardware.

This distinction arises because Verilog is a hardware modelling language and not a hardware implementation language like ABLE, AHDL, CUPL, or PALASM. Its concepts are designed to satisfy its need to model hardware in any manner that allows a model of the hardware to be efficiently simulated. HW synthesis using Verilog, on the other hand, has a completely different set of requirements.

The result is that when combinatorial logic is described in an always block, the target of the assignments must be declared as having type reg. Because reg can hold a value, then if the assignment is incomplete, Verilog maintains the variable's value. From a simulation perspective, this is a natural extension of the fact that variables of type reg can hold values. From a synthesis perspective, the only way for a combinatorial signal to holds its value is for that value to be stored in a bi-stable element. In other words, a latch must be inferred by the synthesizer in order to hold the value of the incompletely specified combinatorial signal.
Quote:
- All sequential logic and latches use non-blocking assignments.

This is the style that I use. No latches except explicitly defined latches. Inferred latches are very BAD.
Quote:
- All combinational logic in an always block uses blocking assignments.

Regardless of combinatorial or FF logic, I use non-blocking assignments in always blocks. Perhaps there's a subtlety regarding efficiency of the simulators that is enhanced by using blocking assignments that I haven't grasped, but in HW all assignments are non-blocking anyways.
Quote:
- All mixed sequential and combinational logic in the same always block uses non-blocking assignments.

I have never been able to create such a beasty. I can't think of how I could accomplish this feat in synthesizable RTL. This recommendation is most likely directed at simulation, which for me is a secondary concern. But I write a simulation for almost every RTL module I create.
Quote:
- Blocking and non-blocking assignments should not be used in the same always block.

This is a synthesizer syntax error as I demonstrated in an earlier post. Not an issue for RTL.
Quote:
- Assignments made to a variable should only be done within one always block.

Easy way to get an inferred latch.
Quote:
- The $strobe system call should only be used to display variables that were assigned using non-blocking assignments. (Not currently implemented)

Kind of hard to read variables and output them to a file or stdout when they are not all being updated by the simulation at the same time. Another reason to consider using non-blocking assignments in an always block regardless of whether the target is a combinatorial or FF signal.

_________________
Michael A.


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 10, 2012 3:07 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
Michael, please could you elaborate on
"Easy way to get an inferred latch."
- I can't tell whether you are agreeing or disagreeing with the guidance
"- Assignments made to a variable should only be done within one always block."
?

I have to say, I have a mental block on the terms "blocking assignment" and "non-blocking assignment" - I have no conceptual difficulty with "immediate" or "scheduled" but the terms themselves bring absolutely nothing to my feeling for whether I need "=" or "<=" - for that reason my ideal one-page crib would not use either term, but would use examples.

For me, there is a place for formal learning of terminology, which is in a course, a lecture or a book, but a crib sheet need not be such a place. I don't say this to criticise your elaboration of the ideas behind the guidelines, but to emphasise what I'd like in that ideal crib sheet.

Cheers
Ed


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 10, 2012 3:36 am 
Offline
User avatar

Joined: Mon Apr 23, 2012 12:28 am
Posts: 760
Location: Huntsville, AL
That is to say that I agree with the guidance. Making assignments in two or more blocks to that same variable is likely to result in a incomplete definition of the variable's state. The result will be that a latch will need to be inferred by the synthesizer.

    - Use variables of type wire to synthesize simple combinatorial logic, or to connect instantiated modules together
    - Use variables of type reg to synthesize complex combinatorial logic or registered logic.
    - Use blocking assignments (=) in assign statements.
    - Use non-blocking assignments (<=) in always blocks.
    - Use always @(*) or always @* for all combinatorial logic.
    - Use always @(posedge Clk) for all registered logic with synchronous resets
    - Use always @(posedge Clk or posedge Rst) for all registered logic with asynchronous resets
    - Always completely define the logic (otherwise a latch is inferred)
      - include a default case when using case() endcase for combinatorial logic
      - include an else clause when using if () for combinatorial logic

_________________
Michael A.


Last edited by MichaelM on Sat Nov 23, 2013 7:58 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 10, 2012 8:34 am 
Offline

Joined: Mon Mar 02, 2009 7:27 pm
Posts: 3258
Location: NC, USA
Sorry to jump in.
MichaelM wrote:
...- Use always @(*) or always @* for all combinatorial logic...

Aha, I was trying to find info on that one thanks!
MichaelM wrote:
...- Always completely define the logic (otherwise a latch is inferred)
    - include a default case when using case() endcase for combinatorial logic
    - include an else clause when using if () for combinatorial logic

So to be sure we completely defined the logic, we check the RTL schematic. I've been doing this alot very recently.

_________________
65Org16:https://github.com/ElEctric-EyE/verilog-6502


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 10, 2012 9:33 am 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
In addition to checking the schematic (which is a lot of work for a big design), it's good practice to scan the warnings. The ISE tools will warn you whenever latches are inferred, and in the warning window you can use the 'Find' feature to quickly find any reference to "latch".


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 62 posts ]  Go to page Previous  1, 2, 3, 4, 5  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 21 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: