Skip to content

Mammal: In Verilog

//mammal CPU
module mammal (
      input clk,
      input [15:0] data_in,
      output logic [15:0] data_out,
      output logic [11:0] address,
      output memwt,
      input INT,
      output intack
);
logic [11:0] pc,ir; //program counter, instruction register
logic [11:0] interruptreg; //to hold temporary isr address during interrupt processing
logic [4:0] state; //FSM
logic [15:0] regbank [7:0];//registers
logic zeroflag; //zero flag register
logic intflag; //interrupt flag
logic [15:0] result; //output for ALU
logic zeroresult;

localparam FETCH=5'b00000,
LDI=5'b00001,
LD=5'b00010,
ST=5'b00011,
JZ=5'b00100,
JMP=5'b00101,
ALU=5'b00111,
PUSH=5'b01000,
POP1=5'b01001,
CALL=5'b01010,
RET1=5'b01011,
STI=5'b01100,
CLI=5'b01101,
IRET1=5'b01110,

POP2= 5'b10000,
RET2= 5'b10001,
IRET2=5'b10010,
IRET3=5'b10011,
INT1= 5'b10100,
INT2= 5'b10101,
INT3= 5'b10110;

always_ff @(posedge clk)
begin
    case(state)
    FETCH:
    begin
        if ( {1'b0,data_in[15:12] }==JZ)
            if (zeroflag)
               state <= JMP;
            else
               state <= FETCH;
        else
            state <= { 1'b0, data_in[15:12] };
        ir<=data_in[11:0];
        pc<=pc+1;
    end

    LDI:
    begin
        regbank[ ir[2:0] ] <= data_in;
        pc<=pc+1;
       if ( intflag & INT )
              state <= INT1;
       else
              state <= FETCH;
      end

     LD:
     begin
         regbank[ir[2:0]] <= data_in;
         if ( intflag & INT )
              state <= INT1;
         else
              state <= FETCH;
      end

      ST:
      begin
          if ( intflag & INT )
              state <= INT1;
         else
              state <= FETCH;
      end

      JMP:
      begin
           pc <= pc+ir;
           if ( intflag & INT )
                state <= INT1;
           else
                state <= FETCH;
       end

       ALU:
       begin
           regbank[ir[2:0]]<=result;
           zeroflag<=zeroresult;
           if ( intflag & INT )
                  state <= INT1;
           else
                  state <= FETCH;
           end

      PUSH:
      begin
           regbank[7]<=regbank[7]-1;
           if ( intflag & INT )
               state <= INT1;
           else
               state <= FETCH;
      end

      POP1:
      begin
           regbank[7]<=regbank[7]+1;
           state <= POP2;
      end

     POP2: //it is possible to eliminate pop2 by adding a register
     begin
           regbank[ir[2:0]] <= data_in;
           if ( intflag & INT )
                state <= INT1;
           else
                state <= FETCH;
      end

CALL:
begin
regbank[7]<=regbank[7]-1;
pc<=pc+ir;
if ( intflag & INT )
state <= INT1;
else
state <= FETCH;
end

RET1:
begin
regbank[7]<=regbank[7]+1;
state <= RET2;
end

RET2:
begin
pc<=data_in[11:0];
if ( intflag & INT )
state <= INT1;
else
state <= FETCH;
end

INT1:
begin
regbank[7]<=regbank[7]-1;
intflag <= 0;
state <= INT2;
end

INT2:
begin
regbank[7]<=regbank[7]-1;
interruptreg <= 12'(data_in+16'h07f0);
state <= INT3;
end

INT3:
begin
pc<=data_in[11:0];
state <= FETCH;
end

IRET1:
begin
regbank[7]<=regbank[7]+1;
state <= IRET2;
end

IRET2:
begin
regbank[7]<=regbank[7]+1;
zeroflag <= data_in[0];
state <= IRET3;
end

IRET3:
begin
pc <= data_in;
if ( intflag & INT )
state <= INT1;
else
state <= FETCH;
end

STI:
begin
intflag <= 1;
if ( intflag & INT )
state <= INT1;
else
state <= FETCH;
end

CLI:
begin
intflag <= 0;
state <= FETCH;
end

endcase
end

//==============address========================
always_comb
case (state)
LD: address=regbank[ir[5:3]][11:0];
ST: address=regbank[ir[5:3]][11:0];
PUSH: address=regbank[7][11:0];
POP2: address=regbank[7][11:0];
CALL: address=regbank[7][11:0];
RET2: address=regbank[7][11:0];
INT1: address=regbank[7][11:0];
INT2: address=regbank[7][11:0];
INT3: address=interruptreg;
IRET1: address=regbank[7][11:0];
IRET2: address=regbank[7][11:0];
IRET3: address=regbank[7][11:0];
default: address=pc;
endcase

//================memwt===================================
assign memwt=(state==ST)||(state==PUSH)||(state==CALL) || (state==INT1) || (state==INT2);

//=================intack================================
always_comb
begin
case (state)
INT2: intack = 1'b1;
default: intack = 1'b0;
endcase
end
//================data_out=============================
always_comb
begin
case (state)
CALL: data_out = {4'b0,pc};
INT1: data_out = {4'b0,pc};
INT2: data_out = {15'b0, zeroflag};
default: data_out = regbank[ir[8:6]];
endcase
end

always_comb //ALU Operation
case (ir[11:9])
3'h0: result = regbank[ir[8:6]]+regbank[ir[5:3]]; //000
3'h1: result = regbank[ir[8:6]]-regbank[ir[5:3]]; //001
3'h2: result = regbank[ir[8:6]]&regbank[ir[5:3]]; //010
3'h3: result = regbank[ir[8:6]]|regbank[ir[5:3]]; //011
3'h4: result = regbank[ir[8:6]]^regbank[ir[5:3]]; //100
3'h7: case (ir[8:6])
3'h0: result = !regbank[ir[5:3]];
3'h1: result = regbank[ir[5:3]];
3'h2: result = regbank[ir[5:3]]+1;
3'h3: result = regbank[ir[5:3]]-1;
default: result=16'h0000;
endcase
default: result=16'h0000;
endcase

assign zeroresult = ~|result;

initial
begin
state=FETCH;
zeroflag=0;
pc=0;
intflag=0; //at the start, interrupts are disabled. programmer must open them by a sti.
end
endmodule