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.