verilogsystem-verilogfpga

Module that converts ASCII to 7-segment display using FPGA


I have a question about the meaning of having a begin statement for each letter. Why is it necessary or is it? Also, what does the HexSeg[x] = y mean for the letters with a begin statement? I am having difficulty wrapping my head around it. Is all of this necessary and is there an easier way to go about this? Is it the same method for coding ASCII numbers? I am also using the DE10-Lite FPGA board.

//
// ASCII to 7-segment display conversion
//
module ASCII27Seg (input [7:0] AsciiCode, output reg [6:0] HexSeg);
    always @ (*) begin
        HexSeg = 7'd0;      // initialization of variable HexSeg
        $display("AsciiCode %b", AsciiCode);
        case (AsciiCode)
            // A
            8'h41 : HexSeg[3] = 1;
            // a
            8'h61 : HexSeg[3] = 1;
            // B
            8'h42 : begin
                HexSeg[0] = 1; HexSeg[1] = 1;
            end
            // b
            8'h62 : begin
                HexSeg[0] = 1; HexSeg[1] = 1;
            end
            // C
            8'h43 : begin
                HexSeg[1] = 1; HexSeg[2] = 1; HexSeg[6] = 1;
            end
            // c
            8'h63 : begin
                HexSeg[1] = 1; HexSeg[2] = 1; HexSeg[6] = 1;
            end
//
// the rest of the code should go here
//
            // D
            8'h44 : begin

            // d
            8'h64 : begin

            // E
            8'h45 : begin

            // e
            8'h65 : begin

            // F
            8'h46 : begin

            // f
            8'h66 : begin

            // G
            8'h47 : begin

            // g
            8'h67 : begin

            // H
            8'h48 : begin

            // h
            8'h68 : begin

            // I
            8'h49 : begin

            // i
            8'h69 : begin

            // J
            8'h4A : begin

            // j
            8'h6A : begin

            // K
            8'h4B : begin

            // k
            8'h6B : begin

            // L
            8'h4C : begin

            // l
            8'h6C : begin

            // M
            8'h4D : begin

            // m
            8'h6D : begin

            // N
            8'h4E : begin

            // n
            8'h6E : begin

            // O
            8'h4F : begin

            // o
            8'h6F : begin

            // P
            8'h50 : begin

            // p
            8'h70 : begin

            // Q
            8'h51 : begin

            // q
            8'h71 : begin

            // R
            8'h52 : begin

            // r
            8'h72 : begin

            // S
            8'h53 : begin

            // s
            8'h73 : begin

            // T
            8'h54 : begin

            // t
            8'h74 : begin

            // U
            8'h55 : begin

            // u
            8'h75 : begin

            // V
            8'h56 : begin

            // v
            8'h76 : begin

            // W
            8'h57 : begin

            // w
            8'h77 : begin

            // X
            8'h58 : begin

            // x
            8'h78 : begin

            // Y
            8'h59 : begin

            // y
            8'h79 : begin

            // Z
            8'h5A : begin

            // z
            8'h7A : begin

            // turn all the bits off by default
            default : HexSeg = 8'b11111111;     // default of variable HexSeg
// you have to finish this code using correct syntax, see begin and end, and case
// and endcase in language specification, and be careful.
        endcase
    end
endmodule

I tried implementing the begin statements for every letter, but I don't understand why it wasn't necessary for 'A' and 'a'. I also don't know how to form the begin statements with HexSeg[x]. Any help would be appreciated.


Solution

  • The begin/end keywords are required by Verilog whenever you have more than one statement in a block of code. They are optional if you only have one statement in the block. Refer to IEEE Std 1800-2017, section 12.5 Case statement.

    Each case item in the case statement can execute a block of code. For example, 8'h41 is the 1st case item. Since that item only has one statement (HexSeg[3] = 1;), the begin/end keywords are not needed. However, for the 8'h42 item, you have 2 statements (HexSeg[0] = 1; HexSeg[1] = 1;), and the begin/end keywords are needed.

    what does the HexSeg[x] = y mean for the letters with a begin statement?

    It means the same thing with or without the begin; you are just setting bits of HexSeg.

    Since some of the items require at least 2 statements, you must use begin/end for those items, as just described. However, from a consistent code style perspective, you might prefer to have all items look the same. Here is how that would look:

    module ASCII27Seg (input [7:0] AsciiCode, output reg [6:0] HexSeg);
        always @ (*) begin
            HexSeg = 7'd0;
            case (AsciiCode)
                // A
                8'h41 : begin 
                    HexSeg[3] = 1;
                end
                // a
                8'h61 : begin
                    HexSeg[3] = 1;
                end
                // B
                8'h42 : begin
                    HexSeg[0] = 1; HexSeg[1] = 1;
                end
                // b
                8'h62 : begin
                    HexSeg[0] = 1; HexSeg[1] = 1;
                end
                // C
                8'h43 : begin
                    HexSeg[1] = 1; HexSeg[2] = 1; HexSeg[6] = 1;
                end
                // c
                8'h63 : begin
                    HexSeg[1] = 1; HexSeg[2] = 1; HexSeg[6] = 1;
                end
                // turn all the bits off by default
                default : begin
                    HexSeg = 8'b11111111;
                end
            endcase
        end
    endmodule
    

    This is a little more typing, but it may be easier to read and understand using this style.

    is there an easier way to go about this?

    This is a good straightforward approach. Using a case statement is a common practice for this sort of design.

    Is it the same method for coding ASCII numbers?

    Yes, if you need to do a similar mapping between 8-bit ASCII and 7-bit HexSeg.


    Whenever you are setting consecutive bits, you could combine them into a single statement using a range specifier. This is optional. For example:

            8'h43 : begin
                    HexSeg[2:1] = 2'b11; HexSeg[6] = 1; // 2 statements
    

    Whenever you execute the same statements for multiple case items, you can combine the items, separating them by commas. For example, a and A execute the same statements:

    module ASCII27Seg (input [7:0] AsciiCode, output reg [6:0] HexSeg);
        always @ (*) begin
            HexSeg = 7'd0;
            case (AsciiCode)
                // A, a
                8'h41, 8'h61 : begin
                    HexSeg[3] = 1;
                end
                // B, b
                8'h42, 8'h62 : begin
                    HexSeg[0] = 1; HexSeg[1] = 1;
                end
                // C, c
                8'h43, 8'h63 : begin
                    HexSeg[1] = 1; HexSeg[2] = 1; HexSeg[6] = 1;
                end
                // turn all the bits off by default
                default : begin
                    HexSeg = 8'b11111111;
                end
            endcase
        end
    endmodule