Applications 11

De la WikiLabs

Requirement

Design, verify and implement a calculator. It adds, subtracts or multiplies a 6 bit number to/from the current content of its accumulator. It continuously display the input number (on the first two display digits) and the current content of the accumulator (on the last three digits of the display). The accumulator's range is from 0 to 255.


Description

The heart of the calculator is the CTRL block. It receives commands from the push buttons (PLUS, MINUS, MULTIPLY or CLEAR), and sends control signals to the datapath. Apart from CTRL there are 6 registers, one for storing the result (the accumulator), and one for each of the digits used for display. The inputs shown in red are the control signals generated by the CTRL. The blocks colored in blue are sequential, clocked by the 50 MHz input clock and initialized/cleared by the common reset input. The reset is assimilated to the CLEAR command.

Calc.png

The output display configuration is stored in 5 registers which are updated cyclically, with a rate of around 100 Hz. In order to use one BCD converter and one BCD-to-segment converter, the input and accumulator values, and their digits are multiplexed digit by digit. A simple solution may be based on a counter from which 3 bits are used to generate the required control signals, as in the figure below:

Appl11 mux wave.png

Design hints

  • For the BCD converter see Exercise 3 from Applications 4. Adapt that solution such that to convert an 8 bit binary number to a three digit BCD number.
  • For the ROM module see Exercise 1 from Applications 8, or Applications 10
The inputs bmul, bsub and badd from the push buttons, being active 0, may be transformed to single pulse internal signals, in order to easy the design of CTRL:
reg  button_d; // delayed button input
wire button_p; // one clock cycle pulse
always @(posedge clk)
    button_d = button;
assign button_p = button & ~button_d; // one clock cycle pulse generated after the push button was released
  • The accumulator and the FUNC block that computes its next value may be combined in a single always process:
reg [7:0] accumulator;
always @(posedge clk) begin
    if(~rst)
        accumulator <= 0;
    else if(lda) begin
        case(selfunc)
        ADD: accumulator <= accumulator + din;
        ....
        endcase
    end
    else
        accumulator <= accumulator;
end
  • If the inputs from push buttons are transformed to one clock cycle pulses, and supposing that the user presses one button at a time, these pulses may be directly used to control the accumulator update and to select the function of the FUNC block:
assign lda = badd_p | bsub_p | bmult_p;
assign selfunc = {badd_p,bsub_p,bmult_p};
  • selfunc values may be defined as localparameters (ADD, SUB etc) to make the code more readable and manageable.
  • To control the display update a simple counter may be used:
reg [31:0] cnt;
assign selnr  = cnt[20];
assign seldig = cnt[19:18];
always @(posedge clk) begin
    case(cnt[20:18])
        3'b000: dig0 <= romdout;
        3'b001: dig1 <= romdout;
        ....
    endcase
end
  • The multiplexers may be described inside the top module with continuous assignments:
assign w2 = sel ? w1 : w0;