While I was following along with Duane's blogs (some time ago, now), I had already created the ring counter that I mentioned in my previous blog.
I was poking around some of the capabilities in the ISE (integrated software environment) and I stumbled upon something interesting. Well, a couple of "somethings," in fact. I think maybe one of those somethings is important, but maybe not. That will be for you to decide.
I'll talk about these somethings in my next column. Today, however, I thought I'd share some of what I've learned in the schematic editor without reading all of the documentation. (This is "Manly Man Rule #47," I think -- read instructions only as a last resort.)
In Duane's example, he instantiated a register that was 26 bits deep. His code added 1 to the register at each rising edge of the clock, so, in effect, it was a simple binary counter. He then pulled off the upper 6 bits of the counter to display on his LEDs, thereby giving us a nice slow display to observe.
In the ring counter I talked about last week, I did basically the same thing, only I wired it up as a classical counter in the schematic editor. Digging way back to my college days (shoot, maybe back to my technician days), I seemed to recall that the way to build a digital counter is to feed the "Q!" ("not Q") output from flip-flop "N" into the "D" ("data") input of flip-flop "N," and also into the "CLK" input of flip-flop "N+1," so that's how I built it up.
As a part of building this project, I learned some things about making symbols in the schematic editor that I'd like to share with you. I started off thinking that I'd just "drop a bunch of flops" into the schematic, but it didn't take me very long to decide that this would quickly become very busy, unreadable, and prone to errors.
After pondering the situation for a while, I "thunk" to myself "There must be a better way to do this," and lo, I stumbled around and figured it out! I created a schematic called "ClockDivider" that was based on the structure shown in the figure below (click here to see a larger, more detailed version of this image):
Lesson learned: ISE sometimes (but not always) chokes on modules that start with numbers!
I could then select the whole mess by clicking and dragging over it all; copy and paste five times (kind of neatly); and wire up the six flops I ended up with a "CLK_IN" signal and six divided "CLK_DIV_x" signals as illustrated below (click here to see a larger, more detailed version of this image):
To create a hierarchical symbol from the schematic, you can select the schematic you've created in the "Design > Hierarchy" pane, and then in the "Process" pane you can select "Create Schematic Symbol" as illustrated below. ISE will very niftily create a symbol for you (this beats the heck out of manual symbol creation!).
Now right-mouse-click and select "Run" (you'll be prompted to save changes if you haven't already done so). After you've created a symbol, you can add that symbol as a part in a different schematic. Click the "Add Part" tool button, find your part, and drop it in. Under "Categories" you'll find a path to your working directory as illustrated below. The symbol will be in there. In fact, any symbol you create will be living in that folder. I've not yet figured out how to make a "global" directory that I can reference from anywhere -- I can see the use, but I'm not real concerned about it right now.
I dropped in several clock dividers and wired their "CLK_DIV_64" output to the next guy's "CLK_IN" input as illustrated below (click here to see a larger, more detailed version of this image):
I almost didn't bother putting an input buffer on the input from the PLL input clock net ("CLK1"). I'd like to hear from some of the experts -- how necessary are the IObuffs? What are the benefits of using these and what are the pitfalls (if any). Is it a really bad idea not to buffer I/O, or are there cases where it's a good idea not to do that? And if you don't explicitly instantiate an IObuff, will the compiler implicitly put one in for you?
The ring counter schematic is as illustrated below (click here to see a larger, more detailed version of this image):
I originally didn't set "init" (initialization) states for the flops -- I figured I might get a random state that cycled. Instead, I always got all zeros. That is, my counter apparently did nothing. I dug around a bit and found that you can set the init state of a flop by double-clicking on it. That will bring up the "Object Properties" dialog as illustrated below (click here to see a larger, more detailed version of this image):
As an aside, you can click here to access a compressed ZIP file containing all of the schematics. Now, generate a configuration bit file and load it into the FPGA with the FrontPanel utility. You'll see the LEDs immediately start flashing. You'll also probably notice that the outputs on the XEM board are inverted (the LEDs are pulled up, remember?).
In my next column, we'll look at the differences between Duane's purely Verilog implementation and my schematic implementation. Hopefully this has been slightly useful so far -- has it? I've been reading along, here on the site, and I've heard it loosely stated that "Schematics are a useful visualization tool, but they are bad, bad, bad! All real engineers capture their designs in HDL code." This made me prick up my ears a bit -- why is this so bad? I understand that when you're drawing wires, the nets names are mostly useless for later investigation, but if you have the schematic, who cares? Is this the crux of the badness, or is there something else we should be made aware of?
Also, let me ask about the initialization of flops in a design. Do the compilers always reset every flop to zero (unless otherwise stated), or do we need to explicitly initialize every variable in a module every time?
Sorry for generating confusion... Yes, what was going on in my head was that flops are single bits, while variables might be multiple bits. I understand that combinatorial logic may not have a "memory" element to it.
When you use the term "variable" in a VHDL discussion, that word has a specific meaning. The number of bits in a variable is irrelevant; it can be one bit or as many as you like.
And as has been discussed, a variable can synthesize into a storage element (or array of storage elements), or it can be absorbed as combinatorial logic in front of a flip-flop's D input. (Of course, that applies to signals, too ...)
Yes, a flip-flop is one bit, but in a behavioral model we often use vectors of bits (generally as signals, sometimes as variables). As for the state register without an initializer or a reset. We know that, in absence of an initializer, the storage elements will be '0' at the end of FPGA configuration. But if your state machine is one-hot coded, then one of the bits in your state register must be set at all times otherwise you are in an invalid state.
If you've set the compiler switch to force a safe state machine, a clock after configuration is released the machine should jump to its safe-default state and continue. But then there's a simulation/synthesis mismatch; in simulation the state register will be 'U' and the machine will be locked up. That's no good.
And of course if you don't set the "safe machine" switch, you're dead in the water. There's nothing to "propagate through the system until the pipe is filled."
Then clocks are applied after some predetermined wait that's more than long enough to ensure eveything propogates to where it's supposed to before the clocks start running?
That's not what happens at all ... how is anything supposed to "propagate to where it's supposed to be?"
The point, of course, is that all state machines need an initializer and/or a reset.
One thing that's common is to use a DCM's LOCKED output indicator as a reset. You've gotta be clever, though, because as we all know a synchronous reset is best. I do something like this:
signal reset_sr : std_logic_vector(15 downto 0) := (others => '1');
alias sReset : std_logic is reset_sr(reset_sr'left);
MakeSyncReset : process (clk, LOCKED) is
begin
if (locked = '0') then
reset_sr <= (others => '1');
elsif rising_edge(clk) then
reset_sr <= reset_sr(reset_sr'left - 1 downto 0) & '0';
end if;
end process MakeSyncReset;
Machine : process(clk) is
begin
if rising_edge(clk) then
if sReset = '1' then
state <= S_INIT;
else
Decoder : case state is
...
end case Decoder;
end if;
end if;
end process Machine;
Note that clk is the DCM's output. So when the DCM is unlocked, the state machine is hung because that output clock is not reliable and may not even be running. The sync reset is also asserted but if the clock isn't running it's not recognized by the state machine process.
Also, when the DCM is unlocked, the little shift register is immediately preset to all '1', asserting the active-high reset. Once the DCM locks, we start shifting in a '0', so after 16 valid clks the shift register output is '0' and the synchronous reset is released.
Thanks, I'll take a look into that in my copious free time! (the free time bit of that is the only part that contains sarcasm - the thanks is heartfelt)
So when you talk about locating the DCM, etc, is there some way you do that in the HDL, or is that done by manually placing, or what?
It's by setting a LOC constraint in the UCF. The main reason to instantiate the BUFG is it basically forces you to give the instance a label (make it useful!), so it's easy to find in PlanAhead or whatever you use to set the location constraint. The DCM has to be instantiated, so you have to give it an instance label, too.
And yes, for every S3E and S3A/AN design I've done which uses a DCM attached to the input clock pin, for some reason unless the BUFG on the input clock (between the IBFUG or IBUFGDS and the DCM's CLKIN) is specifically located, the placer ALWAYS puts that BUFG on the other side of the chip. EVERY TIME.
Which is ridiculous: the documentation is clear on which BUFGs are "allowed" to connect to which input pins and which DCMs, so this hand-location business is a full-on bug.
"Do the compilers always reset every flop to zero (unless otherwise stated), or do we need to explicitly initialize every variable in a module every time?"
You can do both tomii. You can set the levels (pre-defined )or vice versa if you want to.
First, by "variable" I assume that you mean "signal," and when you mean "signal" you really mean "something with storage such as a flip-flip." Why do I make this distinction? Because combinatorial logic has no storage and it will assume an output value immediately after configuration based on the inputs to that logic.
Sorry for generating confusion... Yes, what was going on in my head was that flops are single bits, while variables might be multiple bits. I understand that combinatorial logic may not have a "memory" element to it.
HERE'S A FUN QUESTION. Say we have a state machine which uses one-hot encoding. We know that unless there is an explicit initializer on a signal, it will power up to '0'. So imagine that we don't put that initializer onto our state register. WHAT HAPPENS?
Well, I guess your decoder has an invalid state to start, and then that can propogate through the system until "the pipe is filled." Better have taken steps in other place to ensure you can deal with an invalid state. That brings up an interesting question, though. When we first fire up, everything is, I assume, held low (before the bitstream is loaded). Then we load the bitstream.
...
Then clocks are applied after some predetermined wait that's more than long enough to ensure eveything propogates to where it's supposed to before the clocks start running?
Having said that, the tools that support Spartan 3-generation devices are really pretty stupid and if you are using the DCM, you have to instantiate and locate the BUFG or else the placer will put the buffer on one side of the chip and the DCM on the other and then the mapper will complain.
This is interesting...
I'm well ahead of what's being posted (at least in the sense of blogs), as I ran down multiple different paths. Basically, I figured out the OK interfaces and am now going back and learning the Verilog/VHDL to do things with them. This makes things ... well, convoluted.
So, the other day, I was messing around with DCMs, and I basically got the compiler to complain about what you're talking about - that is, something to the effect of "oh your gawd, you can't possibly really want to use that DCM with that BUFG!!"
I tried changing pins around so that maybe the place-and-route part of the system would maybe drop things near those pins. Yeah, no dice. So I went into the manual place and route tool, and that was not at all intuitive. At least not then. (maybe next time I look at it)
So when you talk about locating the DCM, etc, is there some way you do that in the HDL, or is that done by manually placing, or what
tomii: let me ask about the initialization of flops in a design. Do the compilers always reset every flop to zero (unless otherwise stated), or do we need to explicitly initialize every variable in a module every time?
First, by "variable" I assume that you mean "signal," and when you mean "signal" you really mean "something with storage such as a flip-flip." Why do I make this distinction? Because combinatorial logic has no storage and it will assume an output value immediately after configuration based on the inputs to that logic.
The simple answer is: unless you code an explicit initializer, storage elements in the FPGA (flip-flops, RAMs) configure to '0' because that's what's in the bitstream (as Austin explains). By "explicit initializer," I mean something like this:
signal foo : std_logic := '1'; -- the := '1' is the initializer
Note that the initializer is separate from any reset you might code. It does behoove you, though, that if you code a reset and include an initializer the initial value and the reset value be the same. But the reason that I say this is because there are plenty of cases where a flip-flop needs to be reset at some time other than power-up configuration. And sometimes the synthesizer may realize that a synchronous reset works best even though that's not what you were thinking when you wrote the code. (Nothing wrong with that, of course.)
One more thing. Initializers are important for a simulation, too. Some constructs, especially state machines, need a defined initial condition, otherwise in simulation you'll be in an unknown state and you'll never be able to transition out of it.
HERE'S A FUN QUESTION.
Say we have a state machine which uses one-hot encoding. We know that unless there is an explicit initializer on a signal, it will power up to '0'. So imagine that we don't put that initializer onto our state register. WHAT HAPPENS?
Tomii: - how necessary are the IObuffs? What are the benefits of using these and what are the pitfalls (if any). Is it a really bad idea not to buffer I/O, or are there cases where it's a good idea not to do that? And if you don't explicitly instantiate an IObuff, will the compiler implicitly put one in for you?
First of all, I never do schematics, always VHDL.
For any of the differential IOSTANDARDs, in the Xilinx world you must instantiate the differential buffer. So there's not much of a choice.
For general signal I/O, I never instantiate any buffers. There's no need. The tools do a fine job of it.
For clocks, generally I do not instantiate any buffers.
Having said that, the tools that support Spartan 3-generation devices are really pretty stupid and if you are using the DCM, you have to instantiate and locate the BUFG or else the placer will put the buffer on one side of the chip and the DCM on the other and then the mapper will complain.
You also have to instantiate the special clock buffers for the SERDES (things like BUFIO2, BUFPLL, etc etc). And if you use a DCM or a PLL, again if you instantiate the BUFGs and such it makes it a lot easier to LOC them in the UCF.
I am convinced that every country has its own governmental office buried deep down in some dark, dank basement -- on this office door is a single word: "They!"
In this column we will extend our Opal Kelly FrontPanel interface to have two "okWireOut" devices in the FPGA... or maybe not depending on your point of view.
To save this item to your list of favorite All Programmable Planet content so you can find it later in your Profile page, click the "Save It" button next to the item.
If you found this interesting or useful, please use the links to the services below to share it with other readers. You will need a free account with each service to share an item via that service.