# 8. Design Examples¶

## 8.1. Introduction¶

In previous chapters, some simple designs were introduces e.g. mod-m counter and flip-flops etc. to introduce the Verilog programming. In this chapter various examples are added, which can be used to implement or emulate a system on the FPGA board.

All the design files are provided inside the ‘VerilogCodes’ folder inside the main project directory; which can be used to implement the design using some other software as well. Each section shows the list of Verilog-files require to implement the design in that section. Lastly, all designs are tested using Modelsim and on Altera-DE2 FPGA board. Set the desired design as ‘top-level entity’ to implement or simulate it.

## 8.2. Random number generator¶

In this section, random number generator is implemented using linear feedback shift register. Verilog files required for this example are listed below,

• rand_num_generator.v
• rand_num_generator_visualTest.v
• clockTick.v
• modMCounter.v

Note that, ‘clockTick.v’ and ‘modMCounter.v’ are discussed in Chapter Section 6.

### 8.2.1. Linear feedback shift register (LFSR)¶

Long LFSR can be used as ‘pseudo-random number generator’. These random numbers are generated based on initial values to LFSR. The sequences of random number can be predicted if the initial value is known. However, if LFSR is quite long (i.e. large number of initial values are possible), then the generated numbers can be considered as random numbers for practical purposes.

LFSR polynomial are written as $$x^3 + x^2 + 1$$, which indicates that the feedback is provided through output of ‘xor’ gate whose inputs are connected to positions 3, 2 and 0 of LFSR. Some of the polynomials are listed in Table 8.1.

Table 8.1 List of feedback polynomials
Number of bits} Feedback polynomial}
3 $${x^3} + {x^2} + 1$$
4 $${x^4} + {x^3} + 1$$
5 $${x^5} + {x^3} + 1$$
6 $${x^6} + {x^5} + 1$$
7 $${x^7} + {x^6} + 1$$
9 $${x^9} + {x^5} + 1$$
10 $${x^{10}} + {x^7} + 1$$
11 $${x^{11}} + {x^9} + 1$$
15 $${x^{15}} + {x^{14}} + 1$$
17 $${x^{17}} + {x^{14}} + 1$$
18 $${x^{18}} + {x^{11}} + 1$$

Random numbers are generated using LFSR in Listing 8.1. The code implements the design for 3 bit LFSR, which can be modified for LFSR with higher number of bits as shown below,

Explanation Listing 8.1

The listing is currently set according to 3 bit LFSR i.e. N = 3 in Line 12. ‘q’ is the output of LFSR, which is random in nature. Lines 26-35 sets the initial value for LFSR to 1 during reset operations. Note that, LFSR can not have ‘0’ as initial values. Feedback polynomial is implemented at Line 43. Line 55 shifts the last N bits (i.e. N to 1) to the right by 1 bit and the N^{th} bit is feed with ‘feedback_value’ and stored in ‘r_next’ signal. In next clock cycle, value of r_next is assigned to r_reg through Line 34. Lastly, the value r_reg is available to output port from Line 56.

Simulation results are shown in Fig. Fig. 8.1. Here, we can see that total 7 different numbers are generated by LFSR, which can be seen between two cursors in the figure. Further, q values are represented in ‘hexadecimal format’ which are same as r_reg values in ‘binary format’.

Note

Note that, in Fig. Fig. 8.1, the generated sequence contains ‘8, C, 6, B, 5, 2 and 1’; and if we initialize the system with any of these values, outputs will contain same set of numbers again. If we initialize the system with ‘3’ (which is not the set), then the generate sequence will be entirely different. Fig. 8.1 Random number generation with N = 3

To modify the feedback polynomial, first insert the correct number of bits (i.e. N) in Line 12. Next, modify the feedback_value at line 43, according to new value of ‘N’.

Note

Maximum-length for a polynomial is defined as $$2^N-1$$, but not all the polynomials generate maximum length; e.g. N = 5 generates total 28 sequences (not 31) before repetition as shown in Fig. Fig. 8.2. Fig. 8.2 Total sequences are 28 (not 31) for N = 5}

Listing 8.1 Random number generation with LFSR
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 // rand_num_generator.v // created by : Meher Krishna Patel // date : 22-Dec-16 // Feedback polynomial : x^3 + x^2 + 1 // maximum length : 2^3 - 1 = 7 // if parameter value is changed, // then choose the correct Feedback polynomial i.e. change 'feedback_value' pattern module rand_num_generator #( parameter N = 3 ) ( input wire clk, reset, output wire [N:0] q ); reg [N:0] r_reg; wire [N:0] r_next; wire feedback_value; always @(posedge clk, posedge reset) begin if (reset) begin // set initial value to 1 r_reg <= 1; // use this or uncomment below two line // r_reg <= 1'b1; // 0th bit = 1 // r_reg[N:1] <= 0; // other bits are zero end else if (clk == 1'b1) r_reg <= r_next; end //// N = 3 //// Feedback polynomial : x^3 + x^2 + 1 ////total sequences (maximum) : 2^3 - 1 = 7 assign feedback_value = r_reg ^ r_reg ^ r_reg; //// N = 4 //assign feedback_value = r_reg ^ r_reg ^ r_reg; // N = 5, maximum length = 28 (not 31) //assign feedback_value = r_reg ^ r_reg ^ r_reg; //// N = 9 //assign feedback_value = r_reg ^ r_reg ^ r_reg; assign r_next = {feedback_value, r_reg[N:1]}; assign q = r_reg; endmodule 

### 8.2.2. Visual test¶

Listing 8.2 can be used to test the Listing 8.1 on the FPGA board. Here, 1 second clock pulse is used to visualize the output patterns. Please read Chapter Section 6 for better understanding of the listing. Note that, N = 3 is set in Line 13 according to Listing 8.1.

For displaying outputs on FPGA board, set reset to 1 and then to 0. Then LEDs will blink to display the generated bit patterns by LFSR; which are shown in Fig. Fig. 8.1.

Listing 8.2 Visual test : Random number generation with LFSR
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 // rand_num_generator_visualTest.v // created by : Meher Krishna Patel // date : 22-Dec-16 // if parameter value is changed e.g. N = 5 // then go to rand_num_generator for further modification module rand_num_generator_visualTest #( parameter N = 3 ) ( input wire CLOCK_50, reset, output wire [N:0] LEDR ); wire clk_Pulse1s; // clock 1 s clockTick #(.M(50000000), .N(26)) clock_1s (.clk(CLOCK_50), .reset(reset), .clkPulse(clk_Pulse1s)); // rand_num_generator testing with 1 sec clock pulse rand_num_generator rand_num_generator_1s ( .clk(clk_Pulse1s), .reset(reset), .q(LEDR) ); endmodule 

## 8.3. Shift register¶

Shift register are the registers which are used to shift the stored bit in one or both directions. In this section, shift register is implemented which can be used for shifting data in both direction. Further it can be used as parallel to serial converter or serial to parallel converter. Verilog files required for this example are listed below,

• shift_register.v
• shift_register_visualTest.v
• clockTick.v
• modMCounter.v
• parallel_to_serial.v
• serial_to_parallel.v
• parallel_and_serial_top.v
• parallel_and_serial_top_visual.v

Note that, ‘clockTick.v’ and ‘modMCounter.v’ are discussed in Chapter Section 6.

### 8.3.1. Bidirectional shift register¶

Listing 8.3 implements the bidirectional shift register which is explained below,

Explanation Listing 8.3

In the listing, the ‘ctrl’ port is used for ‘shifting’, ‘loading’ and ‘reading’ data operation. Lines 28 clear the shift register during reset operation, otherwise go to the next state.

Lines 35-40 perform various operations based on ‘ctrl’ values. Note that, to perform right shift (Line 37), data is continuously provided from last port i.e. (data(N-1)); whereas for left shift (Line 38) data is provided from first port i.e. (data(0)).

Next, ctrl=‘00’ is provided for reading the data. It can be used for serial to parallel conversion i.e. when all the bits are shifted and register is full; then set ctrl to ‘00’ and read the data, after that set ctrl to ‘01’ or ‘10’ for getting next set of bits.

Similarly, for parallel to serial converter, first load the data using ctrl=‘11’; and then perform the shift operation until all the bits are read and then again load the data. Note that, in this case, last bit propagates (i.e. data(N-1) for right shift or data(0) for left shift) during shifting; which is actually designed for serial to parallel converter. But this will affect the working of parallel to serial converter, as we will set ctrl to ‘11’, when all the data is shifted, therefore all the register which were filled by values from last port, will be overwritten by the new parallel data.

Lastly, data is available on the output port ‘q_reg’ from Line 43. For, parallel to serial converter, use only one pin of ‘q_reg’ i.e. q_reg(0) for right shift or q(N-1) for left shift; whereas for serial to parallel conversion, complete ‘q_reg’ should be read.

Fig. Fig. 8.3 shows the shifting operation performed by the listing. Here first data ( i.e. 00110000) is loaded with ctrl=‘11’. Then shifted to right after first cursor and later to the left i.e. after second cursor. Fig. 8.3 Right and left shifting operations

Listing 8.3 Bidirectional shift register
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 // shift_register.v // created by : Meher Krishna Patel // date : 22-Dec-16 // Functionality: // load data and shift it data to left and right // parallel to serial conversion (i.e. first load, then shift) // serial to parallel conversion (i.e. first shift, then read) // inputs: // ctrl : to load-data and shift operations (right and left shift) // data : it is the data to be shifted // q_reg : store the outputs module shift_register #( parameter N = 8 ) ( input wire clk, reset, input wire [1:0] ctrl, input wire [N-1:0] data, output wire [N-1:0] q_reg ); reg [N-1:0] s_reg, s_next; always @(posedge clk, posedge reset) begin if(reset) s_reg <= 0; // clear the content else if (clk == 1'b1) s_reg <= s_next; // otherwise save the next state end always @(ctrl, s_reg) begin case (ctrl) 0 : s_next = s_reg; // no operation (to read data for serial to parallel) 1 : s_next = {data[N-1], s_reg[N-1:1]}; // right shift 2 : s_next = {s_reg[N-2:0], data}; // left shift 3 : s_next = data; // load data (for parallel to serial) endcase end assign q_reg = s_reg; endmodule 

Visual test

Listing 8.4 can be used to test the Listing 8.3 on the FPGA board. Here, 1 second clock pulse is used to visualize the output patterns. Here, outputs (i.e. q_reg) are displayed on LEDR; whereas ‘shifting-control (i.e. ctrl)’ and data-load (i.e. data) operations are performed using SW[16:15] and SW[7:0] respectively. Here, we can see the shifting of LEDR pattern twoards right or left based on SW[16:15] combination. Please read Chapter Section 6 for better understanding of the listing.

Listing 8.4 Visual test : bidirectional shift register
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 // shift_register_visualTest.v // created by : Meher Krishna Patel // date : 22-Dec-16 // SW[16:15] : used for control module shift_register_visualTest #( parameter N =8 ) ( input wire CLOCK_50, reset, input wire [16:0] SW, output wire [N-1:0] LEDR ); wire clk_Pulse1s; // clock 1 s clockTick #(.M(50000000), .N(26)) clock_1s (.clk(CLOCK_50), .reset(reset), .clkPulse(clk_Pulse1s)); // shift_register testing with 1 sec clock pulse shift_register #(.N(N)) shift_register_1s ( .clk(clk_Pulse1s), .reset(reset), .data(SW[N-1:0]), .ctrl(SW[16:15]), .q_reg(LEDR) ); endmodule 

### 8.3.2. Parallel to serial converter¶

If data is loaded first (i.e. ctrl = ‘11’), and later shift operation is performed (i.e.. ctrl = ‘01’ or ‘10’); then Listing 8.3 will work as ‘parallel to serial converter’. Listing 8.5 performs the conversion operation, which is tested in Section Section 8.3.4. Please read comments for further details.

Listing 8.5 Parallel to serial conversion
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 // parallel_to_serial.v // Meher Krishna Patel // Date : 26-July-17 // converts parallel data into serial module parallel_to_serial #( parameter N = 8 ) ( input wire clk, reset, input wire [ N-1:0] data_in, // parallel data output reg empty_tick, // for external control output reg data_out // serial data ); reg [N-1:0] data_reg, data_next; reg [N-1:0] count_reg, count_next; reg empty_reg, empty_next; // conversion completed and ready for next data in register always @(posedge clk) empty_tick = empty_reg; // save initial and next value in register always @(posedge clk, posedge reset) begin if(reset) begin count_reg <= 0; empty_reg <= 1; data_reg <= 0; end else begin count_reg <= count_next; empty_reg <= empty_next; data_reg <= data_next; end end always @* begin count_next = count_reg; empty_next = empty_reg; data_next = data_reg; // parallel_to_serial data data_out = data_reg[count_reg]; // coversion completed , load the next data if (count_reg == N-1) begin count_next = 0; // restart count empty_next = 1; data_next = data_in; // load next data end else begin // else continue counting count_next = count_reg + 1; empty_next = 0; end end endmodule 

### 8.3.3. Serial to parallel converter¶

If shifting is performed first (i.e.. ctrl = ‘01’ or ‘10’), and later data is read (i.e. ctrl = ‘00’); then Listing 8.6 will work as ‘serial to parallel converter’. Listing 8.5 performs the conversion operation, which is tested in Section Section 8.3.4. Please read comments for further details.

Listing 8.6 Serial to parallel conversion
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 // serial_to_parallel.v // converts serial data to parallel module serial_to_parallel #( parameter N = 8 ) ( input wire clk, reset, input wire data_in, // serial data output wire full_tick, // for external control output reg [N-1:0] data_out // parallel data ); reg [N-1:0] data_reg, data_next; reg [N-1:0] count_reg, count_next; reg full_reg, full_next; // register is full i.e. parallel data is ready to read assign full_tick = full_reg; // save initial and next value in register always @(posedge clk, posedge reset) begin if(reset) begin count_reg <= 0; full_reg <= 0; data_reg <= 0; end else begin count_reg <= count_next; full_reg <= full_next; data_reg <= data_next; end end always @* begin count_next = count_reg; full_next = full_reg; data_next = data_reg; data_next[count_reg] = data_in; // coversion completed , send data to output if (count_reg == N-1) begin count_next = 0; full_next = 1; // conversion completed, send data to output data_out = data_reg; end else begin // else continue count count_next = count_reg + 1; full_next = 0; end end endmodule 

### 8.3.4. Test for Parallel/Serial converters¶

Here, 4-bit count (i.e. parallel data) is generated using Mod-12 counter. This data is converted into serial data by Listing 8.5; and sent to Listing 8.6, where data is again converted into parallel and the result (i.e. count) is displayed at output as shown in Listing 8.7. The simulation results are shown in Fig. Fig. 8.5. Lastly, visual verification circuit is shown in Listing 8.8. Note that, empty_tick signal is used as clock for modMCounter (see red line in Fig. :numref:fig_parallel_and_serial_design), so that next count will be available when previous conversion is completed. Please read comments for further details.

Listing 8.7 Test for Parallel/Serial converters (results are in Fig. Fig. 8.5)
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 // parallel_and_serial_top.v // test parallel_to_serial.v and serial_to_parallel.v // parallel data (i.e. count from modMCounter) is converted into // serial data using parallel_to_serial.v then tranmitted. // Next, transmitted data is received at serial_to_parallel.v and // converted back to parallel. If everything is working properly then // count should be displayed on 'out_count'. module parallel_and_serial_top( reset, clk, out_count ); input wire reset; input wire clk; output wire [3:0] out_count; wire eclk; wire [3:0] parallel_count; wire serial_count; // count from modMCounter modMCounter unit_counter( .clk(eclk), .reset(reset), .count(parallel_count)); defparam unit_counter.M = 12; defparam unit_counter.N = 4; // count converted into serial data parallel_to_serial unit_p_s( .clk(clk), .reset(reset), .data_in(parallel_count), .empty_tick(eclk), .data_out(serial_count)); defparam unit_p_s.N = 4; // serial data converted back to parallel data serial_to_parallel unit_s_p( .clk(clk), .reset(reset), .data_in(serial_count), .data_out(out_count)); defparam unit_s_p.N = 4; endmodule 
Listing 8.8 Visual test for Parallel/Serial converters
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 // parallel_and_serial_top_visual.v // visual test for parallel_and_serial_top.v module parallel_and_serial_top_visual( reset, CLOCK_50, LEDG ); input wire reset; input wire CLOCK_50; output wire[3:0] LEDG; wire clk1ms; // parallel_and_serial_top parallel_and_serial_top unit_p_s_top( .reset(reset), .clk(clk1ms), .out_count(LEDG)); // 1 ms clock clockTick unit_clk1ms( .clk(CLOCK_50), .reset(reset), .clkPulse(clk1ms)); defparam unit_clk1ms.M = 5000000; defparam unit_clk1ms.N = 23; endmodule 

## 8.4. Random access memory (RAM)¶

RAM is memory cells which are used to store or retrieve the data. Further, FPGA chips have separate RAM modules which can be used to design memories of different sizes and types, as shown in this section. Verilog files required for this example are listed below,

• single_port_RAM.v
• single_port_RAM_visualTest.v
• dual_port_RAM.v
• dual_port_RAM_visualTest.v

### 8.4.1. Single port RAM¶

Single port RAM has one input port (i.e. address line) which is used for both storing and retrieving the data, as shown in Fig. Fig. 8.6. Here ‘addr[1:0]’ port is used for both ‘read’ and ‘write’ operations. Listing 8.9 is used to generate this design.

Explanation Listing 8.9

In the listing port ‘addr’ (Line 23) is 2 bit wide as ‘addr_width’ is set to 2 (Line 17). Therefore, total ‘4 elements (i.e. 2^2)’ can be stored in the RAM. Further, ‘din’ port (Line 24) is 3 bit wide as ‘data_width’ is set to 3 (Line 18); which means the data should be 3 bit wide. In summary, current RAM-designs can store ‘4 elements’ in it and each elements should be 3-bit wide (see declaration at Line 28 as well).

Write enable (we) port should be high and low for storing and retrieving the data respectively. ‘din’ port is used to write the data in the memory; whereas ‘dout’ port is used for reading the data from the memory. In Lines 32-33, the write operation is performed on rising edge of the clock; whereas read operation is performed at Line 37.

Lastly, Fig. Fig. 8.7 shows the simulation results for the design. Here, ‘we’ is set to 1 after first cursor and the data is written at three different addresses (not 4). Next, ‘we’ is set to 0 after second cursor and read operations are performed for all addresses. Since, no values is stored for address ‘10’, therefore dout is displayed as ‘UUU’ for this address as shown after third cursor.

Listing 8.9 Single port RAM
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 // single_port_RAM.v // created by : Meher Krishna Patel // date : 26-Dec-16 // Functionality: // store and retrieve data from single port RAM // ports: // we : write enable // addr : input port for getting address // din : input data to be stored in RAM // data : output data read from RAM // addr_width : total number of elements to store (put exact number) // addr_bits : bits requires to store elements specified by addr_width // data_width : number of bits in each elements module single_port_RAM #( parameter addr_width = 2, data_width = 3 ) ( input wire clk, input wire we, input wire [addr_width-1:0] addr, input wire [data_width-1:0] din, output wire [data_width-1:0] dout ); reg [data_width-1:0] ram_single_port[2**addr_width-1:0]; always @(posedge clk) begin if (we == 1) // write data to address 'addr' ram_single_port[addr] <= din; end // read data from address 'addr' assign dout = ram_single_port[addr]; endmodule 

### 8.4.2. Visual test : single port RAM¶

Listing 8.10 can be use to test the Listing 8.9 on FPGA board. Different combination of switches can be used to store and retrieve the data from RAM. These data will be displayed on LEDs during read operations.

Listing 8.10 Visual test : single port RAM
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 // single_port_RAM_visualTest.v // created by : Meher Krishna Patel // date : 26-Dec-16 // Functionality: // store and retrieve data from single port RAM // ports: // Write Enable (we) : SW // Address (addr) : SW[15-14] // din : SW[2:0] // dout : LEDR module single_port_RAM_visualTest #( parameter ADDR_WIDTH = 2, parameter DATA_WIDTH = 3 ) ( input wire CLOCK_50, input wire [16:0] SW, output wire [DATA_WIDTH-1:0] LEDR ); single_port_RAM single_port_RAM_test( .clk(CLOCK_50), .we(SW), .addr(SW[15:14]), .din(SW[2:0]), .dout(LEDR) ); endmodule 

### 8.4.3. Dual port RAM¶

In single port RAM, the same ‘addr’ port is used for read and write operations; whereas in dual port RAM dedicated address lines are provided for read and write operations i.e. ‘addr_rd’ and ‘addr_wr’ respectively, as shown in Fig. Fig. 8.8. Also, the listing can be further modified to allow read and write operation through both the ports.

Listing 8.11 is used to implement Fig. Fig. 8.8, which is same as Listing 8.9 with three changes. First, two address ports are used at Line 25 (i.e. ‘addr_rd’ and ‘addr_wr’) instead of one. Next, ‘addr_wr’ is used to write the data at Line 35; whereas ‘addr_rd’ data is used to retrieve the data at Line 39. Hence, read and write operation can be performed simultaneously using these two address lines.

Fig. Fig. 8.9 shows the simulation results for dual port RAM. Here, on the first cursor, ‘011’ is written at address ‘01’. On next cursor, this value is read along with writing operation as location ‘10’. Lastly, last two cursor shows that if read and write operation is performed simultaneously on one address e.g. ‘01’, then new data will be available at ‘dout’ port after one clock cycle.

Listing 8.11 Dual port RAM
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 // dual_port_RAM.v // created by : Meher Krishna Patel // date : 26-Dec-16 // Functionality: // store and retrieve data from dual port RAM // ports: // we : write enable // addr_wr : address for writing data // addr_rd : address for reading // din : input data to be stored in RAM // data : output data read from RAM // addr_width : total number of elements to store (put exact number) // addr_bits : bits requires to store elements specified by addr_width // data_width : number of bits in each elements module dual_port_RAM #( parameter addr_width = 2, data_width = 3 ) ( input wire clk, input wire we, input wire [addr_width-1:0] addr_wr, addr_rd, input wire [data_width-1:0] din, output wire [data_width-1:0] dout ); reg [data_width-1:0] ram_dual_port[2**addr_width-1:0]; always @(posedge clk) begin if (we == 1) // write data to address 'addr_wr' ram_dual_port[addr_wr] <= din; end // read data from address 'addr_rd' assign dout = ram_dual_port[addr_rd]; endmodule 

### 8.4.4. Visual test : dual port RAM¶

Listing 8.12 can be use to test the Listing 8.11 on FPGA board. Different combination of switches can be used to store and retrieve the data from RAM. These data will be displayed on LEDs during read operations.

Listing 8.12 Visual test : dual port RAM
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 // dual_port_RAM_visualTest.v // created by : Meher Krishna Patel // date : 26-Dec-16 // Functionality: // store and retrieve data from single port RAM // ports: // Write Enable (we) : SW // Address (addr_wr) : SW[15-14] // Address (addr_rd) : SW[13-12] // din : SW[2:0] // dout : LEDR module dual_port_RAM_visualTest #( parameter ADDR_WIDTH = 2, parameter DATA_WIDTH = 3 ) ( input wire CLOCK_50, input wire [16:0] SW, output wire [DATA_WIDTH-1:0] LEDR ); dual_port_RAM dual_port_RAM_test( .clk(CLOCK_50), .we(SW), .addr_wr(SW[15:14]), .addr_rd(SW[13:12]), .din(SW[2:0]), .dout(LEDR) ); endmodule 

## 8.5. Read only memory (ROM)¶

ROMs are the devices which are used to store information permanently. In this section, ROM is implemented on FPGA to store the display-pattern for seven-segment device, which is explained in Section Section 6.5. Verilog files required for this example are listed below,

• ROM_sevenSegment.v
• ROM_sevenSegment_visualTest.v

### 8.5.1. ROM implementation using RAM (block ROM)¶

Listing 8.13 implements the ROM (Lines 27-47), which stores the seven-segment display pattern in it (Lines 30-45). Since the address width is 16 (Line 16), therefore total 16 values can be stored with different addresses (Lines 30-45). Further, 16 addresses can be represented by 4-bits, therefore ‘addr_bit’ is set to 4 at Line 17. Lastly, total 7 bits are required to represent the number in 7-segment-display, therefore ‘data_width’ is set to 7 at Line 18.

Listing 8.13 Seven segment display pattern stored in ROM
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 //ROM_sevenSegment.v // created by : Meher Krishna Patel // date : 25-Dec-16 // Functionality: // seven-segment display format for Hexadecimal values (i.e. 0-F) are stored in ROM // ports: // addr : input port for getting address // data : ouput data at location 'addr' // addr_width : total number of elements to store (put exact number) // addr_bits : bits requires to store elements specified by addr_width // data_width : number of bits in each elements module ROM_sevenSegment #( parameter addr_width = 16, // store 16 elements addr_bits = 4, // required bits to store 16 elements data_width = 7 // each element has 7-bits ) ( input wire clk, input wire [addr_bits-1:0] addr, output reg [data_width-1:0] data // reg (not wire) ); always @* begin case(addr) 4'b0000 : data = 7'b1000000; // 0 4'b0001 : data = 7'b1111001; // 1 4'b0010 : data = 7'b0100100; // 2 4'b0011 : data = 7'b0110000; // 3 4'b0100 : data = 7'b0011001; // 4 4'b0101 : data = 7'b0010010; // 5 4'b0110 : data = 7'b0000010; // 6 4'b0111 : data = 7'b1111000; // 7 4'b1000 : data = 7'b0000000; // 8 4'b1001 : data = 7'b0010000; // 9 4'b1010 : data = 7'b0001000; // a 4'b1011 : data = 7'b0000011; // b 4'b1100 : data = 7'b1000110; // c 4'b1101 : data = 7'b0100001; // d 4'b1110 : data = 7'b0000110; // e default : data = 7'b0001110; // f endcase end endmodule 

### 8.5.2. Visual test¶

Listing 8.14 is provided the input ‘addr’ through switches ‘SW’ (Line 20) and then output from Listing 8.13 is received to signal ‘data’ (Lines 22-23); which is finally displayed on seven segment display devices (Line 27) and LEDs (Line 28). Here, we can see the switch value on the seven-segment display devices along with the ROM content on LEDs; e.g. if SW value is ‘011’ then ‘3’ will be displayed on seven-segment display and ‘0000110’ will be displayed on LEDs.

Listing 8.14 Display ROM data on seven segment display and LEDs
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 // ROM_sevenSegment_visualTest.v // created by : Meher Krishna Patel // date : 25-Dec-16 // Functionality: // retrieve data from ROM and display on seven-segment device and LEDs // ports: // SW : address in binary format // HEX0 : display data on seven segment device // LEDR : display data on LEDs module ROM_sevenSegment_visualTest ( input wire [3:0] SW, output wire [6:0] HEX0, output wire [6:0] LEDR ); // signal to store received data, so that it can be displayed on // two devices i.e. seven segment display and LEDs wire [6:0] data; ROM_sevenSegment seven_segment_ROM( .addr(SW), .data(data) ); assign HEX0 = data; // display on seven segment devices assign LEDR = data; // display on LEDs endmodule 

## 8.6. Queue with first-in first-out functionality¶

In this section, Queue is designed with first-in first-out functionality. Verilog files required for this example are listed below,

• queue.v
• queue_top.v
• clockTick.v
• modMCounter.v

Note that, ‘clockTick.v’ and ‘modMCounter.v’ are discussed in Chapter Section 6.

### 8.6.1. Queue design¶

Listing 8.15 is the Queue design, which is an example of circular queue. Here, two pointers i.e. Front and rear are used to track the status of the queue i.e. Full or Empty. If read-operation is performed and Front & Rear pointers are at same location, then queue is empty; whereas if write-operation is performed and Front & Rear pointers are at same location, then queue is full. Please read the comments for further details.

Listing 8.15 Queue design
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 // queue.v // queue with first-in first-out operation. module queue #(parameter data_width = 4, // number of bits in each data address_width = 4, // total addresses = 2^4 max_data = 2**address_width // max_data = total_addresses ) ( input wire clk, reset, input wire read_cmd, write_cmd, // read and write command input wire [data_width-1:0] write_data, // write data to FIFO output reg [data_width-1:0] read_data, // read data from FIFO output wire full, // no space to write in FIFO output wire empty // nothing to read from FIFO ); reg [data_width-1:0] queue_reg [max_data-1:0]; reg [address_width-1:0] front_reg, front_next; // pointer-front is for reading reg [address_width-1:0] rear_reg, rear_next; // pointer-rear is for writing reg full_reg, full_next; reg empty_reg, empty_next; assign full = full_reg; assign empty = empty_reg; wire write_enable; // enable if queue is not full assign write_enable = write_cmd & ~full_reg; always @(posedge clk) begin // if queue_reg is full, then data will not be written if (write_enable) queue_reg[rear_reg] <= write_data; if (read_cmd) read_data <= queue_reg[front_reg]; end // status of the queue and location of pointers i.e. front and rear always @(posedge clk, posedge reset) begin if (reset) begin empty_reg <= 1'b1; // empty : nothing to read full_reg <= 1'b0; // not full : data can be written front_reg <= 0; rear_reg <= 0; end else begin front_reg <= front_next; rear_reg <= rear_next; full_reg <= full_next; empty_reg <= empty_next; end end // read and write operation always @* begin front_next = front_reg; rear_next = rear_reg; full_next = full_reg; empty_next = empty_reg; // no operation for {write_cmd, read_cmd} = 00 // only read operation if({write_cmd, read_cmd} == 2'b01) begin // write = 0, read = 1 if(~empty_reg) begin // not empty full_next = 1'b0; // not full as data is read front_next = front_reg + 1; if (front_next == rear_reg) // empty empty_next = 1'b1; end end // only write operation else if ({write_cmd, read_cmd} == 2'b10) begin // write = 1, read = 0 if(~full_reg) begin // not full empty_next = 1'b0; rear_next = rear_reg + 1; if(rear_next == front_reg) full_next = 1'b1; end end // both read and write operation else if ({write_cmd, read_cmd} == 2'b11) begin // write = 1, read = 1 front_next = front_reg + 1; rear_next = rear_next + 1; end end endmodule 

### 8.6.2. Visual test¶

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 // queue_top.v // write the 'count' from 'modMCounter' to the queue. // SW : read operation // LEDR : queue empty // SW : write operation // LEDR  : queue full // Test : // 1. Keep reset high, SW low and SW high till LEDR glow, i.e. // queue is empty now. // 2. Now, keep SW high, SW low, then bring reset to low. // Now count will start and will be stored in the queue. // 3. Bring the SW to low again and see the count at // that time (as count will continue to inrease). // 4. Now, set SW high, and it will start the count from 0, to the count which was // displayed in step 3; and after that LEDR will glow again. // 5. Next, bring SW to low and SW to high, and let it run. // after 16 count (i.e. 2^address_width), the queue will be full and // LED will glow. module queue_top( reset, CLOCK_50, SW, LEDG, LEDR ); input wire reset; input wire CLOCK_50; input wire [1:0] SW; output wire [3:0] LEDG; output wire [17:0] LEDR; wire clk1s; wire [3:0] count; assign LEDR[17:14] = count[3:0]; // clock 1 sec clockTick unit_clkTick( .clk(CLOCK_50), .reset(reset), .clkPulse(clk1s)); defparam unit_clkTick.M = 50000000; defparam unit_clkTick.N = 26; // queue.v queue unit_queue( .clk(clk1s), .reset(reset), .read_cmd(SW), .write_cmd(SW), .write_data(count), .full(LEDR), .empty(LEDR), .read_data(LEDG)); defparam unit_queue.address_width = 4; defparam unit_queue.data_width = 4; defparam unit_queue.max_data = 16; // mod-12 counter modMCounter unit_counter( .clk(clk1s), .reset(reset), .count(count)); defparam unit_counter.M = 12; defparam unit_counter.N = 4; endmodule