Following my previous blog on the continuing saga of my attempt to create an SPI (serial peripheral interface) in my Spartan 6 LX9 FPGA development board, I received an incredible amount of fantastic feedback in the form of comments on this project. The first thing I'm going to do is take advantage of some of this knowledge.
The first order of business, based on Hamster's advice, is to get rid of the second clock resource that I generated with the DCM. What I had done was use the DCM to buffer the system clock and to generate a second clock for the SPI. The DCM on my chip won't create a clock any slower than 400KHz, but I need a 100KHz clock for the SPI. To do this, I fed the slowed clock into a divide-by-four counter. Now I'm going to replace that with a single buffered 100 MHz system clock and a divide-by-1,000 (10-bit) counter to generate my 100KHz SPI clock.
With the above clock gone, I use a 9-bit counter and roll it over on the binary value 0b1111101000. This results in an even 100KHz SPI clock that doesn't consume a second clock resource. Then Devel@latke.net reminded me that I really need double clock speed, because I'm toggling my SPI clock value on each counter rollover. If my counter runs at the desired frequency, I'll only get half a wave for each counter rollover. That means I really need a divide by 500, or a rollover value of 0b111110100.
Next, I took some advice from Paul Clark about getting the lines set up and clocking that data out. Paul suggested a counter with four steps more than the number of data bits. In his example, he had 32 bits of data with a 36-step count. I have 8 bits of data so I'll have a 12-step counter.
The "SS"/"Slave Select" (a.k.a. "CS"/"Chip Select") signal for SPI is active low, so I need to get my "SS" signal to a logic 1 value as soon as possible upon system power-up. I've been digging, but I haven't yet found a way to set the power up default state of an output pin, so I have to set this first thing, however... Is this thinking like a software person? In the FPGA hardware world, if I put in the command "SS = 1;", or better yet, just declare it with a default value "reg SS = 1;", doesn't this happen instantly at power up, not even waiting for a clock? Maybe I've been overthinking this one.
I'm only worrying about outputting data at the moment. I'll deal with receiving data from the slave another time. To get 12 steps, I need a 4-bit register that counts from 0000 to 1011 in binary:
The "always" block needs to clock on the SPI clock and not on the system clock:
This should only cause things to happen when there is data to send. At the moment, I'm just going to clock a byte out if I push the SW5 switch on the board. I'll have that set the "data_ready_flag" and I'll surround the following code with an "if" statement as follows:
Next, I want to do different things based on where I am in the shift counter. At zero, I'll set the chip select low; at 0001, I'll send the SPI clock out the "SCLK" line. I have an enable ("SPIclkEn") and, elsewhere, I AND this with the SPI clock ("assign SCLKio = SCLK & SPIclkEn;"), thereby allowing me to control when the clock is going out to the slave. I also need to have the MSB (most significant bit) ready for when the clock next goes high:
I now need to clock the data out until the LSB (least-significant bit) has been transmitted. The above two "ifs" have performed slightly different actions, while the next seven will perform the same action as follows:
At this point the data byte has been transmitted. Now I need to shut off the clock, set the slave select high again, and reset the "shiftCount" register as follows:
All of this ended up being only a 10-step process. Maybe I'll need a few more when I receive data as well. The next step is to use the ChipScope virtual logic analyzer to take a look at the signals and see what I'm missing and where. I've included all of my source code, in this ZIP file on the off-chance you'd like to take a look at it and provide more before I get to that step.
"On the one hand, it seems like an FPGA should be configurable with an initial power-up state" - Originally, I was thinking in therms of having, say, an MCU on the same PCB as an FPGA. If the MCU is ready first, it might be confused by the FPGA start up port values. However, it's easy enough to just have the MCU wait until the FPGA is up before trying to talk to it. That's something I do all the time -- micro and FPGA talking to each other.
You can avoid confusing the micro if you are careful, and that's easy to do. First, the Xilinx FPGAs have a pin called HSWAPEN or PUDC (depending on family) which control whether the FPGA's user I/O are tristated or weakly pulled up during configuration. Set this to whichever makes sense for the application.
Most micros have enough port i/O pins available so you can dedicate one to an "FPGA valid" signal from the FPGA. That pin will have some undriven (by the FPGA) state based on HSWAPEN and any external pullup/pulldown you might have. When the FPGA finishes configuration and it's ready for action (which might mean waiting until a DCM locks or whatever) the FPGA will drive that signal to whatever state you call "active."
The micro can then spin on that signal after booting, or whatever.
Lots of ways to deal with this, all of which are application-dependent.
Duane Benson 12/11/2012 4:42:35 PM User Rank Blogger
Re: Are we having fun yet?
"On the one hand, it seems like an FPGA should be configurable with an initial power-up state" - Originally, I was thinking in therms of having, say, an MCU on the same PCB as an FPGA. If the MCU is ready first, it might be confused by the FPGA start up port values. However, it's easy enough to just have the MCU wait until the FPGA is up before trying to talk to it.
On the one hand, it seems like an FPGA should be configurable with an initial power-up state, but when I remind myself that the FPGA knows nothing until its bitstream has been loaded at turn-on, it becomes moot. It's better practice anyway to just design the whole system to deal with unknown states at power up.
You have to remember that asserting PROGRAM_B to force reconfiguration of a Xilinx FPGA is essentially an FPGA global reset. So when configuration completes, it is perfectly reasonable to assume that it has been reset in the logic sense. As such, initializers as part of signal declarations are an excellent tool to use.
We typically use a reset supervisor to control PROGRAM_B. Obviously it makes good sense to wait until power supplies are stable before allowing configuration to start. There's no point in adding another reset supervisor to control an FPGA's global (designed-in) reset for obvious reasons. Also a situation which requires a full FPGA reset most likely requires full reconfiguration.
So unless there is some Real Good Reason to provide a full-chip logic reset in addition to what you get from reconfiguration, you don't ned that full-chip reset.
That doesn't mean that localized resets for blocks of logic are not necessary; often they are needed and you should provide them if so. One might use a DCM's locked output signal is a reset; if the clock isn't valid the logic isn't either so this makes sense. (You might want to push the DCM locked signal into a shortish shift register and use the shift register output as a synchronous reset. Whatever.) HAVING SAID ALL OF THAT: Jez is absolutely correct in that some FPGA families (such as Actel; I recently did a ProASIC-3L design) don't have this sort of end-of-configuration reset. The thing with the Actel flash-based FPGAs is that they configure essentially immediately after power-up, and yes, all of the logic blocks come up as zero, so using a reset supervisor to drive a global reset net makes sense. The reset supervisor guarantees that the logic reset remains asserted long after the supplies have stabilized and the FPGA configures.
So -- does using an initializer in this way make the code less portable? Probably. Does this matter? Likely not. Any organization with a proper source-code control system can track different versions of a module without much trouble.
Duane Benson 12/11/2012 10:29:56 AM User Rank Blogger
Re: Are we having fun yet?
Jezmo - That makes sense. I do recall reading about sim and pre-setting non-zero values.
On the one hand, it seems like an FPGA should be configurable with an initial power-up state, but when I remind myself that the FPGA knows nothing until its bitstream has been loaded at turn-on, it becomes moot. It's better practice anyway to just design the whole system to deal with unknown states at power up.
Yeah it is kind of variable as to which tools and devices allow you to do that I have been working with actel devices recently which do have issues with defining initial values
And its always good to have a signal you can wiggle to set everythin to a known state
MartinThompson 12/11/2012 7:26:59 AM User Rank Clever Clogs
Re: Are we having fun yet?
Not true anymore - XST at least can take the init value and put it in the bitstream so that when the FPGA comes out of init, the FFs have the correct initialisation to match the simulation behaviour.
It's been this way since at least ISE 10. See page 462 of the old user manual:
When you give a register an initial value in a declaration, XST sets this value on the output of the register at global reset, or at power up. The assigned value is carried in the NGC file as an INIT attribute on the register, and is independent of any local reset.
what you find is if you define a signal with an initial non-zero value like this
signal :blah stg_logic_vetor(3 downto 0):="1001";
then it will get set to that value in simulation, but in the actual hardware, part of the configuration cycle in the FPGA is that all registers which can be are reset to 0, this happens before the logic is actualy running and responding to input signals.So if you want to have registers start with some non-zero value, usualy you define a 'reset' signal which isnt really a reset, which is designed to come up some time after the logic has been configured and is running, which acts to set any registers whch need to be set.
you will find that ISE mumbles about this and tells you that registers are all starting at zero.
Duane Benson 12/10/2012 9:25:09 PM User Rank Blogger
Re: Are we having fun yet?
Max - I am having a lot of fun with this. Much of the digital logic knowledged that I'm drawing on comes from waaaay back. But there's some good stuff under all those cob webs.
Hi Duane! I don't know Verilog, but I'll try to code for it...
I usually structure my code like this:
if (SPI_clk_div[8:0] == 9'b111110011) begin
spi_clk <= ~spi_clk; // Toggle spi clock every tick
SPI_clk_div[8:0] <= 9'b000000000;
else
SPI_clk_div <= SPI_clk_div + 1;
end
This makes it obvious that the counter only gets incremented when it is not at its terminal count, and at its termainal count it gets reset. This reminds me of the "default assignment are good / bad" holy wars earlier on...
I used 111110011 rather than 111110100 in my example, as you want your counter to go from 0 to 499, not 0 to 500. The "if" will be using the original value of SPI_clk_div, not the incremented one assigned on the line before....
Max Maxfield 12/10/2012 5:30:58 PM User Rank Blogger
Are we having fun yet?
Hi Duane -- I get the impression that you are having a lot of fun learning all of thi sstuff and implementing things like your own SPI core from scratch -- I hope this is the case (that you are indeed enjoying yourself :-)
Duane has decided that the time is ripe to get his ZedBoard bolted onto his robot with a Linux distribution up and running. That was the ultimate plan anyway, so why wait?
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.