My First FPGA Project — Larson Scanner
- June 15th, 2011
- Posted in FPGA . LEDs
- Write comment
I’ve decided to take a little break from MSP430 and learn something new. FPGA’s have always intrigued me, so I picked up a Nexys 2 from Digilent and started reading a book called FPGA Prototyping by Verilog Examples by Pong P. Chu. (That reminds me, I still need to finish Pong for the MSP430.)
I read several chapters to familiarize myself with the hardware and language, but nothing beats learning by doing. My summer construction job was rained out today, so I decided to grab a Rockstar energy drink and write my first Verilog project. I figured a Kitt light/Larson Scanner/Cylon eye would be something I could figure out in a few hours. I’m not too sure how “correct” my code is, so I’d love to hear some suggestions.
module main
(
input wire clk,
output wire [2:0] position,
output wire direction,
output wire [7:0] Led
);
// symbolic state declaration
localparam rising = 1'b1,
falling = 1'b0;
reg [24:0] position_state_count, brightness_state_count;
reg dir_state_reg, dir_state_next;
reg [2:0] position_state_reg, position_state_next;
reg [3:0] brightnesses [7:0];
// reg [2:0] brightnesses_next [7:0];
// state register
always @(posedge clk)
begin
dir_state_reg <= dir_state_next;
position_state_reg <= position_state_next;
end
// next position state logic
always @(posedge clk)
if(position_state_count == 7500000)
begin
position_state_count = 0;
if(dir_state_reg)
position_state_next = position_state_reg + 1;
else
position_state_next = position_state_reg - 1;
end
else
position_state_count = position_state_count + 1;
// next direction state logic
always @(posedge clk)
if(position_state_reg == 7)
dir_state_next = falling;
else if(position_state_reg == 0)
dir_state_next = rising;
// assign position = position_state_reg;
// assign direction = dir_state_reg;
// brightness state logic
always @(posedge clk)
begin
if(brightness_state_count == 1500000)
begin
brightness_state_count = 0;
if(brightnesses[0] > 0)
brightnesses[0] = brightnesses[0] - 1;
if(brightnesses[1] > 0)
brightnesses[1] = brightnesses[1] - 1;
if(brightnesses[2] > 0)
brightnesses[2] = brightnesses[2] - 1;
if(brightnesses[3] > 0)
brightnesses[3] = brightnesses[3] - 1;
if(brightnesses[4] > 0)
brightnesses[4] = brightnesses[4] - 1;
if(brightnesses[5] > 0)
brightnesses[5] = brightnesses[5] - 1;
if(brightnesses[6] > 0)
brightnesses[6] = brightnesses[6] - 1;
if(brightnesses[7] > 0)
brightnesses[7] = brightnesses[7] - 1;
end
else
brightness_state_count = brightness_state_count + 1;
brightnesses[position_state_reg] = 15;
end
// instantiate led pwm logic
pwm led_ctl0 (.brightness(brightnesses[0]), .out(Led[0]), .clk(clk));
pwm led_ctl1 (.brightness(brightnesses[1]), .out(Led[1]), .clk(clk));
pwm led_ctl2 (.brightness(brightnesses[2]), .out(Led[2]), .clk(clk));
pwm led_ctl3 (.brightness(brightnesses[3]), .out(Led[3]), .clk(clk));
pwm led_ctl4 (.brightness(brightnesses[4]), .out(Led[4]), .clk(clk));
pwm led_ctl5 (.brightness(brightnesses[5]), .out(Led[5]), .clk(clk));
pwm led_ctl6 (.brightness(brightnesses[6]), .out(Led[6]), .clk(clk));
pwm led_ctl7 (.brightness(brightnesses[7]), .out(Led[7]), .clk(clk));
endmodule
module pwm
(
input wire clk,
// 16 levels of brightness
input wire [3:0] brightness,
output wire out
);
reg [2:0] counter_reg;
wire [2:0] counter_next;
always @(posedge clk)
counter_reg <= counter_next;
assign counter_next = counter_reg + 1;
assign out = (brightness == 4'b1111) ? 1'b1 :
(brightness > counter_reg) ? 1'b1 : 1'b0;
endmodule
Constraints for Nexys 2:
# clock pin for Nexys 2 Board
NET "clk" LOC = "B8"; # Bank = 0, Pin name = IP_L13P_0/GCLK8, Type = GCLK, Sch name = GCLK0
# Leds
NET "Led<0>" LOC = "J14"; # Bank = 1, Pin name = IO_L14N_1/A3/RHCLK7, Type = RHCLK/DUAL, Sch name = JD10/LD0
NET "Led<1>" LOC = "J15"; # Bank = 1, Pin name = IO_L14P_1/A4/RHCLK6, Type = RHCLK/DUAL, Sch name = JD9/LD1
NET "Led<2>" LOC = "K15"; # Bank = 1, Pin name = IO_L12P_1/A8/RHCLK2, Type = RHCLK/DUAL, Sch name = JD8/LD2
NET "Led<3>" LOC = "K14"; # Bank = 1, Pin name = IO_L12N_1/A7/RHCLK3/TRDY1, Type = RHCLK/DUAL, Sch name = JD7/LD3
NET "Led<4>" LOC = "E17"; # Bank = 1, Pin name = IO, Type = I/O, Sch name = LD4? s3e500 only
NET "Led<5>" LOC = "P15"; # Bank = 1, Pin name = IO, Type = I/O, Sch name = LD5? s3e500 only
NET "Led<6>" LOC = "F4"; # Bank = 3, Pin name = IO, Type = I/O, Sch name = LD6? s3e500 only
NET "Led<7>" LOC = "R4"; # Bank = 3, Pin name = IO/VREF_3, Type = VREF, Sch name = LD7? s3e500 only

Hey,
I’m interested in getting into FPGAs. Would you recommend the Nexys 2 together the book? What are your thoughts on both so far?
Thanks.
As just a learning tool, the Nexys 2 might have been a little overkill. I haven’t played with the RAM and ROM chips, and I have barely scratched the surface of the 500K Spartan’s capabilities. Still, it’s nice to know I have room to grow. It probably depends on what you’re planning. If you wanted to do something really parallel, then you’d probably have a bunch of duplicate modules running in the chip, and that could fill it up pretty quick. Overall though, I’m quite satisfied with the quality and usability of the Nexys 2.
The book was good for getting started. It teaches by example, which fits my learning style. When I first started coding, I mainly worked off the examples, and they make a good framework for my own project (a synthesizer.) The book was given to me, so I can’t say I shopped around. There could be better books out there, but it was fine for me.
In teaching yourself Verilog and FPGA dev, be ready for a challenge. If you’re a programmer, you could compare it to learning assembly — it’s really simple and low level, but that also makes it complicated to use. It’s difficult to debug, and sometimes it takes a lot of work to do a task that would be simple in C with a microcontroller. Don’t let me discourage you though. Once you start getting comfortable, it’s not too confusing. It also gave me a different perspective on how to plan and implement my ideas. I think when I go back to C and microcontrollers, I will be a much better and more organized coder.
(just come from HaD as you said you were teaching yourself Verilog over VHDL and I thought I’d see if I could offer some encouragement as it’s a good choice)
Some of the things I’m probably going to suggest are quite “old school” as at work we have a very strong coding style based around verilog-95 but what you’ve written is either verilog-2000 or System Verilog (having the io declaration in the opening of the module definition is one such difference).
The biggest thing is the mixing of blocking and non-blocking assignments (using ‘=’ or ‘<=' ). You want to keep anything that changes on a clock edge to use <= and anything that changes asynchronously (essentially all the time) with just =. You may or not have got to the reason for it in your reading but the simple answer is down to the evaluation order.
Something that we do at work to emphasise it is to keep the two types of logic separate, meaning that the only thing that goes inside something that starts @posedge clk will be an assingment like current_state <= next_state and the logic to determine what next_state is (based on current_state and other external events/wires) will just have something like next_state= 'IDLE_STATE (oh, there's another recommendation, if you have a state machine, define all the states as 'Define statements first and then just use them as it'll make reading it a lot easier).
Reading through your code, the pwm module looks fine, can't see anything wrong with that but I think the are a few gremlins in the main body. I can see what you're trying to code but I think you have one too many things based on posedge clk when it would be clearer to simply continuously calculate it and simply enable the state/value update on an event, something like:
always @(posedge clk or negedge reset)
if (!reset)
direction_state_reg <= 'START_DIRECTION;
if (change_dir)
direction_state_reg <= ~direction_state_reg; // next direction is simply the inverse of the current one
// direction changes when the position is at either end
assign change_dir = (position_state_reg == 7) | (position_state_reg == 0);
Oh, you've probably noticed I've also added a reset term… it's often useful to ensure control registers and state machines start in a known state ;-)
Have a go with trying some of these things & see how you get on. Have fun!
-Sheldon
PS, I'm being lazy and not editing what I've written so I'll say it at the end. I suggested using 'defines for state-machine states, now I think about it, that's old-school thinking and you should really do it with local params (verilog-95 didn't have them, so it had to be done with defines)
Thanks for the input Sheldon! It felt good to hear that a pro developer said my stuff was mostly correct. I think your suggestion of:
“assign change_dir = (position_state_reg == 7) | (position_state_reg == 0);”
works well to illustrate what you mean by evaluation order and ‘=’ vs ‘<=’. If I understand correctly, the gremlin is that I use a clock edge for the direction change, and it’s ambiguous if that will take effect on the correct clock edge, or the following one, since they’re coded as simultaneous. If I used an assign statement, it would be evaluated continuously. I can imagine that it isn’t obvious when I’m blinking LED’s, but if this were a data path there could be corruption. I feel like I understand the concept better now. Much appreciated.
I also have been defining those states in localparams as you’ve mentioned in my more recent work.
I’m working on a little microcontroller project now, but I expect to get back to FPGA when I finish. I’m currently in school, and have been looking at a few job ads. I’ve noticed many of the better jobs require FPGA experience, so I really feel like knowing this stuff will be beneficial. I hope you stop by and drop a line again in the future when I post some more stuff. Your ‘bits’ of guidance are much appreciated!