This module is used to draw strings of characters to the display, one pixel at a time. The module accepts only upper case characters (A-Z), digits (0-9) and space (“ ”). A null character (0) will not be drawn, and all other ASCII values are drawn as a solid rectangle.
There are several different ways to construct strings in SystemVerilog. For example, the following are all equivalent:
logic [39:0] char_string; assign char_string = "HELLO";
logic [39:0] char_string; assign char_string = 40'h48454C4C4F;
logic [39:0] char_string; assign char_string = {"H", "E", "L", "L", "O"};
logic [39:0] char_string; assign char_string = {8'h48, 8'h45, 8'h4C, 8'h4C, 8'h4F};
Module Name = CharDrawer | |||
---|---|---|---|
Parameter | Default Value | Description | |
MAX_CHARS | 16 | The length of the string that the module can display. | |
Port Name | Direction | Width | Description |
clk | Input | 1 | 100 MHz Clock |
reset | Input | 1 | Active-high reset |
enable | Input | 1 | Raise this signal to start drawing. The drawing will continue until finished. To draw a new string you must lower and then raise this signal. |
done | Output | 1 | Active-high, indicating that the string is done drawing |
x_in | Input | 9 | Top-left x-coordinate of drawing region |
y_in | Input | 8 | Top-left y-coordinate of drawing region |
string_in | Input | MAX_CHARS * 8 | ASCII character string to draw, most-significant byte is drawn first. |
x_out | Output | 9 | x-Coordinate of pixel to draw |
y_out | Output | 8 | y-Coordinate of pixel to draw |
Click the link below to download the CharDrawer.sv file.
module CharDrawer # ( parameter MAX_CHARS = 16 )( input wire logic clk, // Clock input wire logic reset, // Active-high reset input wire logic enable, // Start drawing output logic done, // Done drawing input wire logic [8:0] x_in, // Top-left (x,y) input wire logic [7:0] y_in, input wire logic [(MAX_CHARS * 8) - 1:0] string_in, // ASCII character string to draw // MSB is drawn first output logic [8:0] x_out, // Output (x,y) of pixel to draw output logic [7:0] y_out, output logic draw_en // Active-high enable of drawing ); localparam CHAR_COLS = 5; localparam CHAR_ROWS = 5; logic [8:0] x; // Track top-left (x,y) of current character to draw logic [7:0] y; // ROM to lookup pixel map of each character, row by row logic [8:0] pixel_rom_addr; // { 5 bits of char_id, see table below, 3 bits for row #} logic [4:0] pixel_rom_out; // row of pixel data, output from ROM logic [4:0] pixel_row; // Shift register populated with row of pixel data, then shifted out as it is drawn // ROM to lookup width of each character logic [5:0] width_rom_addr; // char_id logic [2:0] width_rom_out; // width of character logic [2:0] char_width; // width of character, saved to register // char_id // ------- // 0-25 = A-Z // 26-35 = 0-9 // 36 = " " (space) // 37 = all unsported characters, except null (0) which is not printed logic [7:0] char_idx; // Index of character being drawn, starts at MAX_CHARS-1, decrements to 0 logic [2:0] col_idx; // Current column being drawn logic [2:0] row_idx; // Current row being drawn logic col_done; // High when on the last column of the row logic row_done; // High when on the last row of the character logic char_done; // High when on the last row and col // State Machine typedef enum {S_INIT, S_NEXT_CHAR, S_READ_ROW, S_SAVE_ROW, S_DRAW_ROW, S_DONE} StateType; StateType cs; logic char_is_null; logic [7:0] curr_char; logic [7:0] char_select; ////////////////////////////////// Output Logic //////////////////////////////////// assign x_out = x + col_idx; assign y_out = y + row_idx; assign draw_en = pixel_row[CHAR_COLS - 1]; assign done = (char_done && char_idx == 0) || (cs == S_NEXT_CHAR && char_is_null && char_idx == 0); ////////////////////////////////// State Machine ////////////////////////////////// always_ff @(posedge clk) begin if (reset) begin cs <= S_INIT; end else begin case (cs) S_INIT: if (enable) cs <= S_NEXT_CHAR; S_NEXT_CHAR: if (~char_is_null) cs <= S_READ_ROW; else if (char_idx == 0) cs <= S_DONE; S_READ_ROW: cs <= S_SAVE_ROW; S_SAVE_ROW: cs <= S_DRAW_ROW; S_DRAW_ROW: begin if (col_done) begin if (row_done) begin if (char_idx == 0) begin cs <= S_DONE; end else begin cs <= S_NEXT_CHAR; end end else begin cs <= S_READ_ROW; end end end S_DONE: if (!enable) cs <= S_INIT; endcase end end ////////////////////////////////// Row/Col/Char Counting ////////////////////////////////// assign col_done = (cs == S_DRAW_ROW) && (col_idx == (char_width - 1)); assign row_done = (cs == S_DRAW_ROW) && (row_idx == (CHAR_ROWS - 1)); assign char_done = col_done && row_done; assign char_select = string_in[char_idx * 8 +: 8]; assign char_is_null = (char_select == 0); always_ff @(posedge clk) begin if (cs == S_NEXT_CHAR) curr_char <= char_select; end // char_idx always_ff @(posedge clk) begin if (cs == S_INIT) char_idx <= MAX_CHARS - 1; else if (char_done) char_idx <= char_idx - 1; else if (cs == S_NEXT_CHAR && char_is_null) char_idx <= char_idx - 1; end // col_idx always_ff @(posedge clk) begin if (cs == S_READ_ROW) col_idx <= 0; else if (cs == S_DRAW_ROW) col_idx <= col_idx + 1; end // row_idx always_ff @(posedge clk) begin if (cs == S_NEXT_CHAR) row_idx <= 0; if (col_done) row_idx <= row_idx + 1; end // x,y always_ff @(posedge clk) begin if (cs == S_INIT) begin x <= x_in; y <= y_in; end else if (char_done) begin x <= x + char_width + 1; end end // Shift register of current row of pixels being drawn always_ff @(posedge clk) begin if (cs == S_INIT) pixel_row <= 0; if (cs == S_SAVE_ROW) pixel_row <= pixel_rom_out; else if (cs == S_DRAW_ROW) pixel_row <= {pixel_row[CHAR_COLS - 2:0], 1'b0}; end ////////////////////////////////// Character ROM Lookups ////////////////////////////////// always_comb begin if (curr_char >= "A" && curr_char <= "Z") begin pixel_rom_addr = {curr_char - "A", row_idx}; width_rom_addr = {curr_char - "A"}; end else if (curr_char >= "0" && curr_char <= "9") begin pixel_rom_addr = {curr_char - "0" + 26, row_idx}; width_rom_addr = {curr_char - "0" + 26}; end else if (curr_char == " ") begin pixel_rom_addr = {8'd36, row_idx}; width_rom_addr = 8'd36; end else begin pixel_rom_addr = {8'd37, row_idx}; width_rom_addr = 8'd37; end end always_ff @(posedge clk) begin if (cs == S_SAVE_ROW) char_width <= width_rom_out; end always_ff @(posedge clk) begin case(pixel_rom_addr) {"A" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"A" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"A" - "A", 3'd2}: pixel_rom_out = 5'b11100; {"A" - "A", 3'd3}: pixel_rom_out = 5'b10100; {"A" - "A", 3'd4}: pixel_rom_out = 5'b10100; {"B" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"B" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"B" - "A", 3'd2}: pixel_rom_out = 5'b11110; {"B" - "A", 3'd3}: pixel_rom_out = 5'b10010; {"B" - "A", 3'd4}: pixel_rom_out = 5'b11110; {"C" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"C" - "A", 3'd1}: pixel_rom_out = 5'b10000; {"C" - "A", 3'd2}: pixel_rom_out = 5'b10000; {"C" - "A", 3'd3}: pixel_rom_out = 5'b10000; {"C" - "A", 3'd4}: pixel_rom_out = 5'b11100; {"D" - "A", 3'd0}: pixel_rom_out = 5'b11000; {"D" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"D" - "A", 3'd2}: pixel_rom_out = 5'b10100; {"D" - "A", 3'd3}: pixel_rom_out = 5'b10100; {"D" - "A", 3'd4}: pixel_rom_out = 5'b11000; {"E" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"E" - "A", 3'd1}: pixel_rom_out = 5'b10000; {"E" - "A", 3'd2}: pixel_rom_out = 5'b11000; {"E" - "A", 3'd3}: pixel_rom_out = 5'b10000; {"E" - "A", 3'd4}: pixel_rom_out = 5'b11100; {"F" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"F" - "A", 3'd1}: pixel_rom_out = 5'b10000; {"F" - "A", 3'd2}: pixel_rom_out = 5'b11100; {"F" - "A", 3'd3}: pixel_rom_out = 5'b10000; {"F" - "A", 3'd4}: pixel_rom_out = 5'b10000; {"G" - "A", 3'd0}: pixel_rom_out = 5'b11110; {"G" - "A", 3'd1}: pixel_rom_out = 5'b10000; {"G" - "A", 3'd2}: pixel_rom_out = 5'b10110; {"G" - "A", 3'd3}: pixel_rom_out = 5'b10010; {"G" - "A", 3'd4}: pixel_rom_out = 5'b11110; {"H" - "A", 3'd0}: pixel_rom_out = 5'b10100; {"H" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"H" - "A", 3'd2}: pixel_rom_out = 5'b11100; {"H" - "A", 3'd3}: pixel_rom_out = 5'b10100; {"H" - "A", 3'd4}: pixel_rom_out = 5'b10100; {"I" - "A", 3'd0}: pixel_rom_out = 5'b10000; {"I" - "A", 3'd1}: pixel_rom_out = 5'b10000; {"I" - "A", 3'd2}: pixel_rom_out = 5'b10000; {"I" - "A", 3'd3}: pixel_rom_out = 5'b10000; {"I" - "A", 3'd4}: pixel_rom_out = 5'b10000; {"J" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"J" - "A", 3'd1}: pixel_rom_out = 5'b00100; {"J" - "A", 3'd2}: pixel_rom_out = 5'b00100; {"J" - "A", 3'd3}: pixel_rom_out = 5'b10100; {"J" - "A", 3'd4}: pixel_rom_out = 5'b11100; {"K" - "A", 3'd0}: pixel_rom_out = 5'b10100; {"K" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"K" - "A", 3'd2}: pixel_rom_out = 5'b11000; {"K" - "A", 3'd3}: pixel_rom_out = 5'b10100; {"K" - "A", 3'd4}: pixel_rom_out = 5'b10100; {"L" - "A", 3'd0}: pixel_rom_out = 5'b10000; {"L" - "A", 3'd1}: pixel_rom_out = 5'b10000; {"L" - "A", 3'd2}: pixel_rom_out = 5'b10000; {"L" - "A", 3'd3}: pixel_rom_out = 5'b10000; {"L" - "A", 3'd4}: pixel_rom_out = 5'b11100; {"M" - "A", 3'd0}: pixel_rom_out = 5'b10001; {"M" - "A", 3'd1}: pixel_rom_out = 5'b11011; {"M" - "A", 3'd2}: pixel_rom_out = 5'b10101; {"M" - "A", 3'd3}: pixel_rom_out = 5'b10001; {"M" - "A", 3'd4}: pixel_rom_out = 5'b10001; {"N" - "A", 3'd0}: pixel_rom_out = 5'b10010; {"N" - "A", 3'd1}: pixel_rom_out = 5'b11010; {"N" - "A", 3'd2}: pixel_rom_out = 5'b10110; {"N" - "A", 3'd3}: pixel_rom_out = 5'b10010; {"N" - "A", 3'd4}: pixel_rom_out = 5'b10010; {"O" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"O" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"O" - "A", 3'd2}: pixel_rom_out = 5'b10100; {"O" - "A", 3'd3}: pixel_rom_out = 5'b10100; {"O" - "A", 3'd4}: pixel_rom_out = 5'b11100; {"P" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"P" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"P" - "A", 3'd2}: pixel_rom_out = 5'b11100; {"P" - "A", 3'd3}: pixel_rom_out = 5'b10000; {"P" - "A", 3'd4}: pixel_rom_out = 5'b10000; {"Q" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"Q" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"Q" - "A", 3'd2}: pixel_rom_out = 5'b10100; {"Q" - "A", 3'd3}: pixel_rom_out = 5'b10100; {"Q" - "A", 3'd4}: pixel_rom_out = 5'b11110; {"R" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"R" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"R" - "A", 3'd2}: pixel_rom_out = 5'b11000; {"R" - "A", 3'd3}: pixel_rom_out = 5'b10100; {"R" - "A", 3'd4}: pixel_rom_out = 5'b10100; {"S" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"S" - "A", 3'd1}: pixel_rom_out = 5'b10000; {"S" - "A", 3'd2}: pixel_rom_out = 5'b11100; {"S" - "A", 3'd3}: pixel_rom_out = 5'b00100; {"S" - "A", 3'd4}: pixel_rom_out = 5'b11100; {"T" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"T" - "A", 3'd1}: pixel_rom_out = 5'b01000; {"T" - "A", 3'd2}: pixel_rom_out = 5'b01000; {"T" - "A", 3'd3}: pixel_rom_out = 5'b01000; {"T" - "A", 3'd4}: pixel_rom_out = 5'b01000; {"U" - "A", 3'd0}: pixel_rom_out = 5'b10100; {"U" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"U" - "A", 3'd2}: pixel_rom_out = 5'b10100; {"U" - "A", 3'd3}: pixel_rom_out = 5'b10100; {"U" - "A", 3'd4}: pixel_rom_out = 5'b11100; {"V" - "A", 3'd0}: pixel_rom_out = 5'b10100; {"V" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"V" - "A", 3'd2}: pixel_rom_out = 5'b10100; {"V" - "A", 3'd3}: pixel_rom_out = 5'b11100; {"V" - "A", 3'd4}: pixel_rom_out = 5'b01000; {"W" - "A", 3'd0}: pixel_rom_out = 5'b10001; {"W" - "A", 3'd1}: pixel_rom_out = 5'b10001; {"W" - "A", 3'd2}: pixel_rom_out = 5'b10101; {"W" - "A", 3'd3}: pixel_rom_out = 5'b11011; {"W" - "A", 3'd4}: pixel_rom_out = 5'b10001; {"X" - "A", 3'd0}: pixel_rom_out = 5'b10100; {"X" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"X" - "A", 3'd2}: pixel_rom_out = 5'b01000; {"X" - "A", 3'd3}: pixel_rom_out = 5'b10100; {"X" - "A", 3'd4}: pixel_rom_out = 5'b10100; {"Y" - "A", 3'd0}: pixel_rom_out = 5'b10100; {"Y" - "A", 3'd1}: pixel_rom_out = 5'b10100; {"Y" - "A", 3'd2}: pixel_rom_out = 5'b11100; {"Y" - "A", 3'd3}: pixel_rom_out = 5'b01000; {"Y" - "A", 3'd4}: pixel_rom_out = 5'b01000; {"Z" - "A", 3'd0}: pixel_rom_out = 5'b11100; {"Z" - "A", 3'd1}: pixel_rom_out = 5'b00100; {"Z" - "A", 3'd2}: pixel_rom_out = 5'b01000; {"Z" - "A", 3'd3}: pixel_rom_out = 5'b10000; {"Z" - "A", 3'd4}: pixel_rom_out = 5'b11100; {"0" - "0" + 26, 3'd0}: pixel_rom_out = 5'b11100; {"0" - "0" + 26, 3'd1}: pixel_rom_out = 5'b10100; {"0" - "0" + 26, 3'd2}: pixel_rom_out = 5'b10100; {"0" - "0" + 26, 3'd3}: pixel_rom_out = 5'b10100; {"0" - "0" + 26, 3'd4}: pixel_rom_out = 5'b11100; {"1" - "0" + 26, 3'd0}: pixel_rom_out = 5'b11000; {"1" - "0" + 26, 3'd1}: pixel_rom_out = 5'b01000; {"1" - "0" + 26, 3'd2}: pixel_rom_out = 5'b01000; {"1" - "0" + 26, 3'd3}: pixel_rom_out = 5'b01000; {"1" - "0" + 26, 3'd4}: pixel_rom_out = 5'b01000; {"2" - "0" + 26, 3'd0}: pixel_rom_out = 5'b11100; {"2" - "0" + 26, 3'd1}: pixel_rom_out = 5'b00100; {"2" - "0" + 26, 3'd2}: pixel_rom_out = 5'b11100; {"2" - "0" + 26, 3'd3}: pixel_rom_out = 5'b10000; {"2" - "0" + 26, 3'd4}: pixel_rom_out = 5'b11100; {"3" - "0" + 26, 3'd0}: pixel_rom_out = 5'b11100; {"3" - "0" + 26, 3'd1}: pixel_rom_out = 5'b00100; {"3" - "0" + 26, 3'd2}: pixel_rom_out = 5'b01100; {"3" - "0" + 26, 3'd3}: pixel_rom_out = 5'b00100; {"3" - "0" + 26, 3'd4}: pixel_rom_out = 5'b11100; {"4" - "0" + 26, 3'd0}: pixel_rom_out = 5'b10100; {"4" - "0" + 26, 3'd1}: pixel_rom_out = 5'b10100; {"4" - "0" + 26, 3'd2}: pixel_rom_out = 5'b11100; {"4" - "0" + 26, 3'd3}: pixel_rom_out = 5'b00100; {"4" - "0" + 26, 3'd4}: pixel_rom_out = 5'b00100; {"5" - "0" + 26, 3'd0}: pixel_rom_out = 5'b11100; {"5" - "0" + 26, 3'd1}: pixel_rom_out = 5'b10000; {"5" - "0" + 26, 3'd2}: pixel_rom_out = 5'b11100; {"5" - "0" + 26, 3'd3}: pixel_rom_out = 5'b00100; {"5" - "0" + 26, 3'd4}: pixel_rom_out = 5'b11100; {"6" - "0" + 26, 3'd0}: pixel_rom_out = 5'b10000; {"6" - "0" + 26, 3'd1}: pixel_rom_out = 5'b10000; {"6" - "0" + 26, 3'd2}: pixel_rom_out = 5'b11100; {"6" - "0" + 26, 3'd3}: pixel_rom_out = 5'b10100; {"6" - "0" + 26, 3'd4}: pixel_rom_out = 5'b11100; {"7" - "0" + 26, 3'd0}: pixel_rom_out = 5'b11100; {"7" - "0" + 26, 3'd1}: pixel_rom_out = 5'b00100; {"7" - "0" + 26, 3'd2}: pixel_rom_out = 5'b00100; {"7" - "0" + 26, 3'd3}: pixel_rom_out = 5'b00100; {"7" - "0" + 26, 3'd4}: pixel_rom_out = 5'b00100; {"8" - "0" + 26, 3'd0}: pixel_rom_out = 5'b11100; {"8" - "0" + 26, 3'd1}: pixel_rom_out = 5'b10100; {"8" - "0" + 26, 3'd2}: pixel_rom_out = 5'b11100; {"8" - "0" + 26, 3'd3}: pixel_rom_out = 5'b10100; {"8" - "0" + 26, 3'd4}: pixel_rom_out = 5'b11100; {"9" - "0" + 26, 3'd0}: pixel_rom_out = 5'b11100; {"9" - "0" + 26, 3'd1}: pixel_rom_out = 5'b10100; {"9" - "0" + 26, 3'd2}: pixel_rom_out = 5'b11100; {"9" - "0" + 26, 3'd3}: pixel_rom_out = 5'b00100; {"9" - "0" + 26, 3'd4}: pixel_rom_out = 5'b00100; {8'd36, 3'd0}: pixel_rom_out = 5'b00000; {8'd36, 3'd1}: pixel_rom_out = 5'b00000; {8'd36, 3'd2}: pixel_rom_out = 5'b00000; {8'd36, 3'd3}: pixel_rom_out = 5'b00000; {8'd36, 3'd4}: pixel_rom_out = 5'b00000; {8'd37, 3'd0}: pixel_rom_out = 5'b11110; {8'd37, 3'd1}: pixel_rom_out = 5'b11110; {8'd37, 3'd2}: pixel_rom_out = 5'b11110; {8'd37, 3'd3}: pixel_rom_out = 5'b11110; {8'd37, 3'd4}: pixel_rom_out = 5'b11110; endcase end always_ff @(posedge clk) begin case(width_rom_addr) // A-Z {"A" - "A"}: width_rom_out = 3; {"B" - "A"}: width_rom_out = 4; {"C" - "A"}: width_rom_out = 3; {"D" - "A"}: width_rom_out = 3; {"E" - "A"}: width_rom_out = 3; {"F" - "A"}: width_rom_out = 3; {"G" - "A"}: width_rom_out = 4; {"H" - "A"}: width_rom_out = 3; {"I" - "A"}: width_rom_out = 1; {"J" - "A"}: width_rom_out = 3; {"K" - "A"}: width_rom_out = 3; {"L" - "A"}: width_rom_out = 3; {"M" - "A"}: width_rom_out = 5; {"N" - "A"}: width_rom_out = 4; {"O" - "A"}: width_rom_out = 3; {"P" - "A"}: width_rom_out = 3; {"Q" - "A"}: width_rom_out = 4; {"R" - "A"}: width_rom_out = 3; {"S" - "A"}: width_rom_out = 3; {"T" - "A"}: width_rom_out = 3; {"U" - "A"}: width_rom_out = 3; {"V" - "A"}: width_rom_out = 3; {"W" - "A"}: width_rom_out = 5; {"X" - "A"}: width_rom_out = 3; {"Y" - "A"}: width_rom_out = 3; {"Z" - "A"}: width_rom_out = 3; // 0-9 {"0" - "0" + 26}: width_rom_out = 3; {"1" - "0" + 26}: width_rom_out = 2; {"2" - "0" + 26}: width_rom_out = 3; {"3" - "0" + 26}: width_rom_out = 3; {"4" - "0" + 26}: width_rom_out = 3; {"5" - "0" + 26}: width_rom_out = 3; {"6" - "0" + 26}: width_rom_out = 3; {"7" - "0" + 26}: width_rom_out = 3; {"8" - "0" + 26}: width_rom_out = 3; {"9" - "0" + 26}: width_rom_out = 3; // Space 36: width_rom_out = 4; // non-supported, non-null 37: width_rom_out = 4; endcase end endmodule