summaryrefslogtreecommitdiff
path: root/eater_cpu/eater_decoder.sv
blob: ba7cf16c309b88f15c1eac47ea30927bee6910e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
`timescale 1us/1us

`include "eater_types.sv"

module eater_decoder (
    input clk_i,
    input wire [7:0] instruction_i,
    output CpuControlFlags flags_o
);

CpuState internal_state /* verilator public_flat_rd */;
CpuState next_state;
CpuControlFlags internal_flags;

wire [3:0] actual_instruction = instruction_i[7:4];

assign flags_o = internal_flags;

initial begin
    internal_state = INIT;
    next_state = INIT;
    // assigned in always_comb
    // internal_flags = '{default: '0};
end

function CpuState insdep_state;
    case (actual_instruction)
    LDA: insdep_state = LDA_INS_to_MAR;
    ADD: insdep_state = ADD_INS_to_MAR;
    OUT: insdep_state = OUT_A_to_OUT;
    HALT_op: insdep_state = HALT_st;

    default: insdep_state = INIT;
    endcase
endfunction

// next-state machine
always @(posedge clk_i) begin
    next_state = INIT;

    case (internal_state)

    INIT: next_state = PC_to_MAR;

    PC_to_MAR: next_state = MEM_to_INS;

    MEM_to_INS: next_state = PC_inc;

    PC_inc: next_state = insdep_state();

    LDA_INS_to_MAR: next_state = LDA_MEM_to_A;
    LDA_MEM_to_A: next_state = PC_to_MAR;

    ADD_INS_to_MAR: next_state = ADD_MEM_to_B;
    ADD_MEM_to_B: next_state = ADD_ALU_to_A;
    ADD_ALU_to_A: next_state = PC_to_MAR;

    OUT_A_to_OUT: next_state = PC_to_MAR;

    HALT_st: next_state = HALT_st;

    default: begin
       next_state = INIT; 
    end

    endcase
    internal_state <= next_state;
end

// current-state-to-flags machine
// TODO: This is kinda weird, I *can* set "all flags" to zero "in the beginning",
// and then only set the required flags "later".
// how does that work combinatorically???
always_comb begin
`ifdef IVERILOG
    internal_flags = 'b0;
`else
    internal_flags = '{default: '0};
`endif

    case (internal_state)
    // INIT: // left out
    PC_to_MAR: begin
        internal_flags.PC_out = 1;
        internal_flags.MAR_in = 1;
    end
    MEM_to_INS: begin
        internal_flags.RAM_out = 1;
        internal_flags.INS_in = 1;
    end
    PC_inc: begin
        internal_flags.PC_count = 1;
    end

    LDA_INS_to_MAR: begin
        internal_flags.INS_out = 1;
        internal_flags.MAR_in = 1;
    end

    LDA_MEM_to_A: begin
        internal_flags.RAM_out = 1;
        internal_flags.A_in = 1;
    end

    ADD_INS_to_MAR: begin
        internal_flags.INS_out = 1;
        internal_flags.MAR_in = 1;
    end

    ADD_MEM_to_B: begin
        internal_flags.RAM_out = 1;
        internal_flags.B_in = 1; 
    end

    ADD_ALU_to_A: begin
        internal_flags.ALU_out = 1;
        internal_flags.A_in = 1;
    end

    OUT_A_to_OUT: begin
        internal_flags.A_out = 1;
        internal_flags.OUT_in = 1;
    end

    HALT_st: begin
        internal_flags.halt = 1;
    end

    default: begin
    end

    endcase
end

endmodule