processor.v
module processor (clock, interrupt, read, write, address, data, ready);

// PARAMETERS

    // SMI INSTANCE NAME

    parameter INSTANCE = "";

    // ADDRESS AND DATA BUS WIDTHS

    parameter ADDR_WIDTH = 16;
    parameter DATA_WIDTH = 16;

// INTERFACE

    input                   clock;
    input                   interrupt;
    output                  read;
    output                  write;
    output [ADDR_WIDTH-1:0] address;
    inout  [DATA_WIDTH-1:0] data;
    input                   ready;

// CONSTANTS

    // MESSAGE TYPE IDENTIFIERS

    parameter CPU_CONFIGURE   = 1;
    parameter CPU_INSTRUCTION = 2;
    parameter CPU_IDLE        = 3;
    parameter CPU_OUT         = 4;
    parameter CPU_IN          = 5;
    parameter CPU_DATA        = 6;
    parameter CPU_INTERRUPT   = 7;
    parameter CPU_IRET        = 8;
    
// INTERNAL SIGNALS

    // READ AND WRITE

    reg rd, wr;

    // INTERRUPT ENABLE

    reg ie;

    // ADDRESS BUS

    reg [ADDR_WIDTH-1:0] addr;

    // DATA BUS

    reg [DATA_WIDTH-1:0] dout;

    // CURRENT INSTRUCTION

    integer instruction;

     // SMI MODEL IDENTIFIER

     integer id;

// IMPLEMENTATION

    initial
    begin
        // SET DEFAULT OUTPUTS

        addr <= 0;
        dout <= 0;
        {rd,wr,ie} <= 3'b001;

        // REGISTER THE MODEL INSTANCE WITH SMI

        $smi_initial(id, "processor", INSTANCE);

        // CONFIGURE THE BUS DIMENSIONS BY SENDING A "Configure" MESSAGE

        $smi_write(id, CPU_CONFIGURE, ADDR_WIDTH, DATA_WIDTH);

        // PRINT THE CONFIGURATION

        $display("MODEL-%d: processor(%s)[ADDRESS WIDTH=%d; DATA WIDTH=%d]", id, INSTANCE, ADDR_WIDTH, DATA_WIDTH);
    end

    always@(posedge clock)
    begin
        // IF INTERRUPTS ARE ENABLED AND AN INTERRUPT IS DETECTED

        if (ie && interrupt)
        begin
            // SEND AN "Interrupt" MESSAGE

            $smi_write(id, CPU_INTERRUPT);

            // DISABLE INTERRUPTS

            ie = 0;
        end

        // FETCH THE NEXT INSTRUCTION

        $smi_read(id, CPU_INSTRUCTION, instruction);

        // CHOOSE AND ACTION DEPENDING ON THE MESSAGE TYPE

        case (instruction)

        // IF THE MESSAGE TYPE IS "Idle"; DO NOTHING FOR A CLOCK CYCLE

        CPU_IDLE: @(posedge clock);

        // IF THE MESSAGE TYPE IS "IRet"; RE-ENABLE INTERRUPTS

        CPU_IRET: ie = 1;

        // IF THE MESSAGE TYPE IS "In"

        CPU_IN:
            begin
                // READ AND SET THE ADDRESS BUS VALUE

                $smi_data(id, addr);
                addr <= addr;

                // ASSERT THE READ SIGNAL

                rd <= 1;

                // CLOCK UNTIL THE OPERATION IS COMPLETE

                @(posedge clock) while (!ready) @(posedge clock);

                // RETURN THE DATA BUS VALUE BY SENDING A "Data" MESSAGE

                $smi_write(id, CPU_DATA, data);

                // DE-ASSERT THE READ SIGNAL

                rd <= 0;
            end

        // IF THE MESSAGE TYPE IS "In"

        CPU_OUT:
            begin
                // READ AND SET THE ADDRESS AND DATA BUS VALUES

                $smi_data(id, addr, dout);
                addr <= addr;
                dout <= dout;

                // ASSERT THE WRITE SIGNAL

                wr <= 1;

                // CLOCK UNTIL THE OPERATION IS COMPLETE

                @(posedge clock) while (!ready) @(posedge clock);

                // DE-ASSERT THE READ SIGNAL

                wr <= 0;
            end
        endcase
    end

    // SET OUTPUT SIGNALS AND BUSSES

    assign read = rd;
    assign write = wr;
    assign address = addr;

    // ONLY DRIVE THE DATA BUS DURING BUS WRITE OPERATIONS

    assign data = wr ? dout : 16'bz;

endmodule
© Copyright 2000-2001 Adrian Lewis