Icebreaker FPGA workshop

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.