Project: risclab1

Contents

Verilog Files

risclab1.v
risc_cpu.v
alu.v
regfile.v
clock_divider.v
divide_by_10.v
divide_by_50.v
hex_7seg_blanking.v
single_port_ram.v

Quartus Files

fit.summary
tan.summary

Verilog Files

risclab1.v

module risclab1(
  // Clock Input (50 MHz)
  input  CLOCK_50,
  //  Push Buttons
  input  [3:0]  KEY,
  //  DPDT Switches 
  input  [17:0]  SW,
  //  7-SEG Displays
  output  [6:0]  HEX0, HEX1, HEX2, HEX3, HEX4, HEX5, HEX6, HEX7,
  //  LEDs
  output  [8:0]  LEDG,  //  LED Green[8:0]
  output  [17:0]  LEDR,  //  LED Red[17:0]
  //  GPIO Connections
  inout  [35:0]  GPIO_0, GPIO_1
);

//  set all inout ports to tri-state
assign  GPIO_0    =  36'hzzzzzzzzz;
assign  GPIO_1    =  36'hzzzzzzzzz;

wire [6:0] myclock;
wire reset_n  = KEY[0];

// Setup clock divider
clock_divider cdiv(CLOCK_50,reset_n,myclock);

assign LEDG[8:1] = 8'h00;
assign LEDG[0] = active;

// Connect dip switches to red LEDS
assign LEDR[17:0] = SW[17:0];


// map to 7-segment displays

wire [3:0] rx;
assign rx = {1'b0, ra[2:0]};

hex_7seg_blanking dsp4(rx,HEX4,SW[3]);


wire [15:0] dx;

hex_7seg_blanking dsp0(dx[3:0],HEX0,1'b0);
hex_7seg_blanking dsp1(dx[7:4],HEX1,1'b0);
hex_7seg_blanking dsp2(dx[11:8],HEX2,1'b0);
hex_7seg_blanking dsp3(dx[15:12],HEX3,1'b0);

wire [6:0] blank = ~7'h00;

assign HEX5 = blank;
//assign HEX6 = blank;
assign HEX7 = blank;
hex_7seg_blanking dsp6({2'h0,state},HEX6,~active);

wire [15:0] qa,qd;
wire [2:0] ra,rd;
assign ra = active? rd: SW[2:0];
assign dx = SW[3]? ncycles: (active? qd: qa);
wire we;

regfile u1(
    .clk(CLOCK_50),
    .ra(ra),
    .qa(qa),
    .rd(rd),
    .we(we),
    .qd(qd)
);

wire clk = (SW[17]? myclock[0]: CLOCK_50);

wire active;
wire [15:0] ncycles;
wire [1:0] state;


risc_cpu cpu0(
    .clk(clk),
    .reset_n(reset_n),
    .rd(rd),
    .qd(qd),
    .rwe(we),
    .cpu_active(active),
    .cpu_state(state),
    .ncycles(ncycles)
);


endmodule

risc_cpu.v

`define OP  2:0 
`define PC 11:4
`define RD 14:12
`define RA 18:16
`define RB 22:20
`define IM 35:24
`define OPHALT 3
`define OPSW 40
`define OPLW 41
`define OPBNE 42
`define OPJALR 43

module risc_cpu 
#(parameter DATA_WIDTH=16, parameter ADDR_WIDTH=5)
(
    input clk, reset_n,
    output [2:0] rd,
    output [(DATA_WIDTH-1):0] qd,
    output rwe,
    output cpu_active,
    output [1:0] cpu_state,
    output reg [(DATA_WIDTH-1):0] ncycles
);

wire [(ADDR_WIDTH-1):0] addr;
wire [2:0] ra, rb;
wire  [(DATA_WIDTH-1):0] q, qi, qa, qb;
wire [47:0] IF;
reg [47:0] ID;
reg we;

assign cpu_active = active;
assign cpu_state = state;

single_port_ram dev1(
    .data(qb),
    .addr(addr),
    .we(we),
    .clk(clk),
    .q(q)
);

assign rwe = state==IFE;

regfile dev2(
    .clk(clk),
    .we(rwe),
    .ra(ra),
    .qa(qa),
    .rb(rb),
    .qb(qb),
    .rd(rd),
    .qd(qd)
);

reg [4:0] pc;
//wire [(DATA_WIDTH-1):0] q;
wire [2:0] opcode;

// Instruction Fetch (IF)

reg [15:0] inst;
reg active;
wire ma2 = (ID[`OPSW] || ID[`OPLW]);
//wire [15:0] qi;
assign qi = (active? (ma2? inst: q): 16'h0000);
assign opcode = qi[15:13];

// opcodes    
`include "opcodes.txt"

wire op_add = opcode==ADD;
wire op_nand = opcode==NAND;
wire op_sw = opcode==SW;
wire op_lw = opcode==LW;
wire op_bne = opcode==BNE;
wire op_jalr = opcode==JALR;

wire flag1 = op_sw||op_bne;   // no write-back
wire flag2 = op_add||op_nand; // RRR format
wire [2:0] rd1 = flag1? 3'h0: qi[12:10];
assign ra = qi[9:7];
assign rb = flag2? qi[2:0]: (flag1? qi[12:10]: 3'h0);

assign IF[`OP] = opcode;
assign IF[`OPHALT] = (qi==16'he071); // is halt
assign IF[`PC] = pc;
assign IF[`RD] = rd1;
assign IF[`RA] = ra;
assign IF[`RB] = rb;
assign IF[`IM] = qi[9:0];
assign IF[`OPSW] = op_sw;
assign IF[`OPLW] = op_lw;
assign IF[`OPBNE] = op_bne;
assign IF[`OPJALR] = op_jalr;


wire memory_access = (IF[`OPSW] || IF[`OPLW]);

//reg [(ADDR_WIDTH-1):0] jump;
wire [(ADDR_WIDTH-1):0] jump = do_jump? qa[(ADDR_WIDTH-1):0]: (ID[`OPBNE]? d1[(ADDR_WIDTH-1):0]: 5'h00);
wire do_branch = ID[`OPBNE] && (qa!=qb);
wire do_jump = ID[`OPJALR];


wire [4:0] nextpc = pc + 1'b1;
wire [4:0] newpc = (do_jump||do_branch? jump: nextpc);

reg [4:0] maddr;
assign addr = (state==MEM? d1: newpc );


wire [15:0] d1;

wire [4:0] npc = ID[`PC] + 1'b1;
assign rd = ID[`RD];

alu dev3(
  .a(qa),
  .b(qb),
  .pc({11'h000,npc}),
  .im10(ID[`IM]),
  .d(d1),
  .opcode(ID[`OP])
);

// writeback register multiplexer
assign qd = (ID[`OPLW]? q: d1);


reg [1:0] state;
localparam
    RESET = 2'h0,
    IFE = 2'h1,
    MEM = 2'h2,
    HALT = 2'h3;

wire [47:0] BLANK = 48'h0000_0000_0000;


always @(posedge clk, negedge reset_n)
begin
  if (!reset_n)
  begin
   active <= 1'b0;
   inst <= 16'h0000;
   pc <= 5'h1F;
   ID <= BLANK;
   state <= RESET;
   end
  else 
    case (state)
    RESET:
    begin
        active <= 1'b0;
        state <= IFE;
    end
    IFE:
    begin
        active <= 1'b1;
        pc <= addr;
        if (do_jump)
        begin
            if (ID[`OPHALT]) state <= HALT;
            ID <= BLANK;
        end
        else if (do_branch) ID <= BLANK;
        else 
        begin
            ID <= IF;
            if (memory_access) 
            begin 
                state <= MEM;
                we <= IF[`OPSW];
            end
        end
    end
    MEM:
    begin
        state <= IFE;
        inst <= q;
        we <= 1'b0;
    end
    default:
    begin
        active <= 1'b0;
        state <= HALT;
    end
  endcase
end

always @(posedge clk or negedge reset_n)
begin
    if (!reset_n) ncycles <= 16'h0000;
    else if (state==IFE || state==MEM) ncycles <= ncycles + 1'b1;
end

endmodule

alu.v


module alu(
  input [15:0] a, b, pc,
  input [9:0] im10,
  output reg [15:0] d,
  input [2:0] opcode
);

`include "opcodes.txt"

wire [15:0] seim = im10[6]? {9'h1FF,im10[6:0]}:{9'h000,im10[6:0]};
wire [15:0] sum = a +  seim;

always
case (opcode)
ADD:
    d <= a + b;
ADDI:
    d <= sum;
NAND:
    d <= ~(a&b);
LUI:
    d <= {im10,6'h00};
SW:
    d <= sum;
LW:
    d <= sum;
BNE:
    d <= pc+seim;
JALR:
    d <= pc;
endcase

endmodule

regfile.v

// The register file is a triple-port RAM
// with two read addresses and a write address.
// It is implemented with two double-port RAMs

module regfile
#(parameter DATA_WIDTH=16, parameter ADDR_WIDTH=3)
(
    input clk,
    input [(ADDR_WIDTH-1):0] ra, rb, rd,
    output [(DATA_WIDTH-1):0] qa, qb,
    input [(DATA_WIDTH-1):0] qd,
    input we
);
    double_port port1(
        .clk(clk),
        .we(we),
        .ra(ra),
        .rd(rd),
        .qa(qa),
        .qd(qd)
    );
    double_port port2(
        .clk(clk),
        .we(we),
        .ra(rb),
        .rd(rd),
        .qa(qb),
        .qd(qd)
    );
endmodule

module double_port 
#(parameter DATA_WIDTH=16, parameter ADDR_WIDTH=3)
(
    input [(DATA_WIDTH-1):0] qd,
    input [(ADDR_WIDTH-1):0] ra, rd,
    input we, clk,
    output [(DATA_WIDTH-1):0] qa
);

    // Declare the RAM variable
    reg [DATA_WIDTH-1:0] ram[0:2**ADDR_WIDTH-1];
    
    // Initialize the RAM with $readmemb.  Put the memory contents
    // in the data file.  Without this file,
    // this design will not compile.
    // See Verilog LRM 1364-2001 Section 17.2.8 for details on the
    // format of this file.

    initial
    begin
        $readmemh("registers.txt", ram);
    end

    // Variable to hold the registered read address
    reg [ADDR_WIDTH-1:0] ra_reg;
    
    always @ (posedge clk)
    begin
        // Write
        if (we)
            ram[rd] <= qd;
    end
    
    always @ (posedge clk)
        ra_reg <= ra;

    // Continuous assignment implies read returns NEW data.
    // This is the natural behavior of the TriMatrix memory
    // blocks in Single Port mode.  
    assign qa = (ra_reg? ram[ra_reg]:16'h0000);

endmodule

clock_divider.v

module clock_divider(CLK,RST,clock);
input CLK,RST;
output [6:0] clock;
wire clk_1Mhz,clk_100Khz,clk_10Khz,clk_1Khz,clk_100hz,clk_10hz,clk_1hz;

assign clock = {clk_1Mhz,clk_100Khz,clk_10Khz,clk_1Khz,clk_100hz,clk_10hz,clk_1hz};

divide_by_50 d6(clk_1Mhz,CLK,RST);
divide_by_10 d5(clk_100Khz,clk_1Mhz,RST);
divide_by_10 d4(clk_10Khz,clk_100Khz,RST);
divide_by_10 d3(clk_1Khz,clk_10Khz,RST);
divide_by_10 d2(clk_100hz,clk_1Khz,RST);
divide_by_10 d1(clk_10hz,clk_100hz,RST);
divide_by_10 d0(clk_1hz,clk_10hz,RST);
endmodule

divide_by_10.v

module divide_by_10(Q,CLK,RST);
input CLK, RST;
output Q;
reg Q;
reg [2:0] count;
always @ (posedge CLK or negedge RST)
    begin
        if (~RST)
            begin
                Q <= 1'b0;
                count <= 3'b000;
            end
        else if (count < 4)
            begin 
                 count <= count+1'b1;
            end
        else 
            begin
                count <= 3'b000;
                Q <= ~Q;
            end
    end
endmodule

divide_by_50.v

module divide_by_50(Q,CLK,RST);
input CLK, RST;
output Q;
reg Q;
reg [4:0] count;
always @ (posedge CLK or negedge RST)
    begin
        if (~RST)
            begin
                Q <= 1'b0;
                count <= 5'b00000;
            end
        else if (count < 24)
            begin 
                 count <= count+1'b1;
            end
        else 
            begin
                count <= 5'b00000;
                Q <= ~Q;
            end
    end
endmodule

hex_7seg_blanking.v

module hex_7seg_blanking(
    input [3:0] hex_digit,
    output [6:0] seg, 
    input blank_display
);

reg [6:0] segm;
// segm = {g,f,e,d,c,b,a};
// 0 is on and 1 is off

always @ (hex_digit)
case (hex_digit)
        4'h0: segm = ~7'h3F;
        4'h1: segm = ~7'h06;     // ---a----
        4'h2: segm = ~7'h5B;     // |      |
        4'h3: segm = ~7'h4F;     // f      b
        4'h4: segm = ~7'h66;     // |      |
        4'h5: segm = ~7'h6D;     // ---g----
        4'h6: segm = ~7'h7D;     // |      |
        4'h7: segm = ~7'h07;     // e      c
        4'h8: segm = ~7'h7F;     // |      |
        4'h9: segm = ~7'h67;     // ---d----
        4'ha: segm = ~7'h77;
        4'hb: segm = ~7'h7C;
        4'hc: segm = ~7'h39;
        4'hd: segm = ~7'h5E;
        4'he: segm = ~7'h79;
        4'hf: segm = ~7'h71;
endcase

wire [6:0] blank = ~7'h00;

//assign blank_out = (hex_digit==4'b0000) && blank_in;
assign seg = (blank_display? blank: segm);

endmodule

single_port_ram.v

// Quartus II Verilog Template
// Single port RAM with single read/write address 

module single_port_ram 
#(parameter DATA_WIDTH=16, parameter ADDR_WIDTH=5)
(
    input [(DATA_WIDTH-1):0] data,
    input [(ADDR_WIDTH-1):0] addr,
    input we, clk,
    output [(DATA_WIDTH-1):0] q
);

    // Declare the RAM variable
    reg [DATA_WIDTH-1:0] ram[0:2**ADDR_WIDTH-1];
    
    // Initialize the RAM with $readmemb.  Put the memory contents
    // in the file single_port_rom_init.txt.  Without this file,
    // this design will not compile.
    // See Verilog LRM 1364-2001 Section 17.2.8 for details on the
    // format of this file.

    initial
    begin
        $readmemh("prog2.txt", ram);
    end

    // Variable to hold the registered read address
    reg [ADDR_WIDTH-1:0] addr_reg;

    always @ (posedge clk)
    begin
        // Write
        if (we)
            ram[addr] <= data;

        addr_reg <= addr;
    end

    // Continuous assignment implies read returns NEW data.
    // This is the natural behavior of the TriMatrix memory
    // blocks in Single Port mode.  
    assign q = ram[addr_reg];

endmodule

Quartus Compilation Summary

fit.summary

Fitter Status : Successful - Wed Apr 21 12:50:34 2010
Quartus II Version : 9.0 Build 235 06/17/2009 SP 2 SJ Web Edition
Revision Name : risclab1
Top-level Entity Name : risclab1
Family : Cyclone II
Device : EP2C35F672C6
Timing Models : Final
Total logic elements : 422 / 33,216 ( 1 % )
    Total combinational functions : 417 / 33,216 ( 1 % )
    Dedicated logic registers : 148 / 33,216 ( < 1 % )
Total registers : 148
Total pins : 178 / 475 ( 37 % )
Total virtual pins : 0
Total memory bits : 896 / 483,840 ( < 1 % )
Embedded Multiplier 9-bit elements : 0 / 70 ( 0 % )
Total PLLs : 0 / 4 ( 0 % )

tan.summary

--------------------------------------------------------------------------------------
Timing Analyzer Summary
--------------------------------------------------------------------------------------

Type           : Worst-case tsu
Slack          : N/A
Required Time  : None
Actual Time    : 4.320 ns
From           : KEY[0]
To             : risc_cpu:cpu0|we
From Clock     : --
To Clock       : CLOCK_50
Failed Paths   : 0

Type           : Worst-case tco
Slack          : N/A
Required Time  : None
Actual Time    : 36.620 ns
From           : risc_cpu:cpu0|regfile:dev2|double_port:port1|altsyncram:ram_rtl_2|altsyncram_p7j1:auto_generated|ram_block1a0~portb_address_reg2
To             : HEX3[0]
From Clock     : CLOCK_50
To Clock       : --
Failed Paths   : 0

Type           : Worst-case tpd
Slack          : N/A
Required Time  : None
Actual Time    : 15.491 ns
From           : SW[3]
To             : HEX3[0]
From Clock     : --
To Clock       : --
Failed Paths   : 0

Type           : Worst-case th
Slack          : N/A
Required Time  : None
Actual Time    : 8.801 ns
From           : KEY[0]
To             : risc_cpu:cpu0|we
From Clock     : --
To Clock       : CLOCK_50
Failed Paths   : 0

Type           : Clock Setup: 'CLOCK_50'
Slack          : N/A
Required Time  : None
Actual Time    : 41.49 MHz ( period = 24.104 ns )
From           : risc_cpu:cpu0|regfile:dev2|double_port:port1|altsyncram:ram_rtl_2|altsyncram_p7j1:auto_generated|ram_block1a0~portb_address_reg2
To             : regfile:u1|double_port:port1|altsyncram:ram_rtl_0|altsyncram_p7j1:auto_generated|ram_block1a0~porta_datain_reg11
From Clock     : CLOCK_50
To Clock       : CLOCK_50
Failed Paths   : 0

Type           : Clock Setup: 'SW[17]'
Slack          : N/A
Required Time  : None
Actual Time    : 97.50 MHz ( period = 10.256 ns )
From           : risc_cpu:cpu0|regfile:dev2|double_port:port1|altsyncram:ram_rtl_2|altsyncram_p7j1:auto_generated|ram_block1a0~portb_address_reg2
To             : risc_cpu:cpu0|regfile:dev2|double_port:port2|altsyncram:ram_rtl_1|altsyncram_p7j1:auto_generated|ram_block1a0~porta_datain_reg15
From Clock     : SW[17]
To Clock       : SW[17]
Failed Paths   : 0

Type           : Clock Hold: 'CLOCK_50'
Slack          : Not operational: Clock Skew > Data Delay
Required Time  : None
Actual Time    : N/A
From           : risc_cpu:cpu0|state.IFE
To             : risc_cpu:cpu0|state.IFE
From Clock     : CLOCK_50
To Clock       : CLOCK_50
Failed Paths   : 4629

Type           : Total number of failed paths
Slack          : 
Required Time  : 
Actual Time    : 
From           : 
To             : 
From Clock     : 
To Clock       : 
Failed Paths   : 4629

--------------------------------------------------------------------------------------


Maintained by John Loomis, last updated Wed Apr 21 12:55:38 2010