I have been working through the icebreaker workshop from Workshop. On the next to the last is a list of optional things to attempt. I decided to change the display to show something other than numbers. I tried Japanese numbers on a 7-segment display even though there are not enough segments to do this justice. With that working I thought “what about having a button toggle between Western and Japanese numbers. This is where I hit a brick wall.
I am at the beginning stages of learning Verilog and HDL. I was thinking of modules as functions. It took a wake up yesterday morning I was thinking programming and not hardware design. I wasn’t passing a variable from one function to the next. I didn’t need global variables. I needed to have wires and inputs and outputs. However, just because I had this realization didn’t mean I knew how to implement it. Looking up errors and warnings on the web AND understanding the answers in blogs and forums was difficult. It was like a foreign language.
I was running into “unconstrained”, “has no driver”, and “implicitly declared”, among others. When I thought I understood how to fix the error or warning I would just move it to another line of code.
I finally got the code for the stopwatch to do what I wanted.
Button 3 starts or stops the count running.
Button 2 pauses and flashes the display.
Button 3 toggles the set of characters to display.
Button N stops the count and sets it back to zero.
(Now I am going to try and paste the entire code in one block without having to deal with it separating itself into multiple block paragraphs.)
// Cause yosys to throw an error when we implicitly declare nets
// `default_nettype none
// Project entry point
module top (
input CLK,
input BTN_N, BTN1, BTN2, BTN3,
output LED1, LED2, LED3, LED4, LED5,
output P1A1, P1A2, P1A3, P1A4, P1A7, P1A8, P1A9, P1A10,
);
// 7 segment control line bus
wire [7:0] seven_segment;
// Assign 7 segment control line bus to Pmod pins
assign { P1A10, P1A9, P1A8, P1A7, P1A4, P1A3, P1A2, P1A1 } = seven_segment;
// Display value register and increment bus
reg [7:0] display_value = 0;
wire [7:0] display_value_inc;
// Lap registers
reg [7:0] lap_value = 0;
reg [4:0] lap_timeout = 0;
reg [7:0] flasher_temp;
reg [2:0] flasher_tick = 0;
// Clock divider and pulse registers
reg [20:0] clkdiv = 0;
reg clkdiv_pulse = 0;
reg running = 0;
reg debounce3 = 0;
reg [2:0] num_choice = 0;
reg debounce1 = 0;
wire dragon;
assign dragon = num_choice;
reg [23:0] counter = 1'b0;
reg [4:0] pulsar = 0;
reg [1:0] lit1 = 1'b0;
reg [1:0] lit2 = 1'b0;
reg [1:0] lit3 = 1'b0;
reg [1:0] lit4 = 1'b0;
reg [1:0] lit5 = 1'b0;
assign LED1 = lit1;
assign LED2 = lit2;
assign LED3 = lit3;
assign LED4 = lit4;
assign LED5 = lit5;
always @(posedge CLK) begin
counter++;
if (counter > 800000) begin
counter = 0;
pulsar++;
if (pulsar > 3) begin
pulsar = 0;
end
end
case (pulsar)
0: begin
lit2 = 1'b1;
lit4 = 1'b0;
end
1: begin
lit2 = 1'b0;
lit5 = 1'b1;
end
2: begin
lit5 = 1'b0;
lit3 = 1'b1;
end
3: begin
lit3 = 1'b0;
lit4 = 1'b1;
end
default: lit5 = 1'b0;
endcase
end
// Synchronous logic
always @(posedge CLK) begin
// Clock divider pulse generator
if (clkdiv == 1200000) begin
clkdiv <= 0;
clkdiv_pulse <= 1;
end else begin
clkdiv <= clkdiv + 1;
clkdiv_pulse <= 0;
end
// Lap Timeout counter
if (clkdiv_pulse && lap_timeout) begin
lap_timeout <= lap_timeout -1;
if (flasher_tick) begin
lap_value = 8'b11111111;
flasher_tick = 0;
end else begin
lap_value = flasher_temp;
flasher_tick = 1;
end
end
// Timer counter
if (clkdiv_pulse && running) begin
display_value <= display_value_inc;
end
// Button controls
if (!BTN_N) begin
display_value <= 0;
running = 0;
lap_timeout <= 0;
end
if (!BTN3) debounce3 = 0;
if (BTN3) begin
if (debounce3 == 0) begin
debounce3 = 1;
if (running) running = 0;
else running = 1;
end
end
if (!BTN1) debounce1 = 0;
if (BTN1) begin
if (!debounce1) begin
debounce1 = 1;
if (num_choice) num_choice = 0;
else num_choice = 1;
end
end
if (BTN2) begin
lap_value <= display_value;
flasher_temp <= display_value;
lap_timeout <= 20;
end
end
// assign display_value_inc = display_value + 8'b1; // take this out for bcd8_increment
// make instance of BCD counter
bcd8_increment increment1 (
.din(display_value),
.dout (display_value_inc)
);
// 7 segment display control Pmod 1A
seven_seg_ctrl seven_segment_ctrl (
.CLK(CLK),
.din(lap_timeout ? lap_value[7:0] : display_value[7:0]),
.dout(seven_segment),
.dragon(dragon),
);
endmodule // End of the Top module
// BCD (Binary Coded Decimal) counter
module bcd8_increment (
input [7:0] din,
output reg [7:0] dout
);
always @* begin
case (1'b1)
din[7:0] == 8'h 99:
dout = 0;
din[3:0] == 4'h 9:
dout = {din[7:4] + 4'd 1, 4'h 0};
default:
dout = {din[7:4], din[3:0] + 4'd 1};
endcase
end
endmodule
// Seven segment controller
// Switches quickly between the two parts of the display
// to create the illusion of both halves being illuminated
// at the same time.
module seven_seg_ctrl (
input CLK,
input [7:0] din,
input dragon,
output reg [7:0] dout
);
wire [6:0] lsb_digit;
wire [6:0] msb_digit;
seven_seg_hex msb_nibble (
.dragon(dragon),
.din(din[7:4]),
.dout(msb_digit)
);
seven_seg_hex lsb_nibble (
.dragon(dragon),
.din(din[3:0]),
.dout(lsb_digit)
);
reg [9:0] clkdiv = 0;
reg clkdiv_pulse = 0;
reg msb_not_lsb = 0;
always @(posedge CLK) begin
clkdiv <= clkdiv + 1;
clkdiv_pulse <= &clkdiv;
msb_not_lsb <= msb_not_lsb ^ clkdiv_pulse;
if (clkdiv_pulse) begin
if (msb_not_lsb) begin
dout[6:0] <= ~msb_digit;
dout[7] <= 0;
end else begin
dout[6:0] <= ~lsb_digit;
dout[7] <= 1;
end
end
end
endmodule
// Convert 4bit numbers to 7 segments
module seven_seg_hex (
input dragon,
input [3:0] din,
output reg [6:0] dout
);
reg num_choice = dragon;
always @*
case (din)
4'h0: begin
if (num_choice) dout = 7'b 1011100;
else dout = 7'b0111111;
end
4'h1: begin
if (num_choice) dout = 7'b 1000000;
else dout = 7'b0000110;
end
4'h2: begin
if (num_choice) dout = 7'b 0001001;
else dout = 7'b1011011;
end
4'h3: begin
if (num_choice) dout = 7'b 1001001;
else dout = 7'b1001111;
end
4'h4: begin
if (num_choice) dout = 7'b 0111111;
else dout = 7'b1100110;
end
4'h5: begin
if (num_choice) dout = 7'b 1111101;
else dout = 7'b1011011;
end
4'h6: begin
if (num_choice) dout = 7'b 0010101;
else dout = 7'b1111101;
end
4'h7: begin
if (num_choice) dout = 7'b 1111000;
else dout = 7'b0000111;
end
4'h8: begin
if (num_choice) dout = 7'b 0110110;
else dout = 7'b1111111;
end
4'h9: begin
if (num_choice) dout = 7'b 1110100;
else dout = 7'b1101111;
end
4'hA: dout = 7'b 1110111;
4'hB: dout = 7'b 1111100;
4'hC: dout = 7'b 0111001;
4'hD: dout = 7'b 1011110;
4'hE: dout = 7'b 1111001;
4'hF: dout = 7'b 0000000;
default: dout = 7'b 0000000;
endcase
endmodule
The icebreaker workshop is being quite the adventure. I think I could join an online Verilog/Yosys forum, but I don’t have enough experience to be able to ask the right questions. Perhaps I will join one once I understand the terminology better.