summaryrefslogtreecommitdiffstats
path: root/hdl/decoder.vhd
blob: 07e785d7031f09764bd92b402c21b9da9c5344ce (plain) (blame)
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
library ieee;
use ieee.std_logic_1164.all;
use work.alu_types.all;

package decoder is
  type instruction_class_t is (
    BIT_OP,
    BYTE_OP,
    LITERAL_OP,
    CONTROL_OP,
    UNKNOWN
    );

  type instruction_t is record
    class : instruction_class_t;
    op    : alu_op;
    f     : std_logic_vector(6 downto 0);
    b     : std_logic_vector(2 downto 0);
    d     : std_logic;
    k     : std_logic_vector(10 downto 0);
  end record;

  pure function instruction_get_class(opc : std_logic_vector(13 downto 0)) return instruction_class_t;

  pure function instruction_get_operation(
    opc         : std_logic_vector(13 downto 0);
    instr_class : instruction_class_t)
    return alu_op;

  pure function instruction_decode(opc : std_logic_vector(13 downto 0)) return instruction_t;

end package;

package body decoder is

  pure function instruction_decode(opc : std_logic_vector(13 downto 0))
    return instruction_t is
    variable instr : instruction_t;
  begin
    instr.class := instruction_get_class(opc);
    instr.op    := instruction_get_operation(opc, instr.class);

    instr.f := (others => '0');
    instr.b := (others => '0');
    instr.d := '0';
    instr.k := (others => '0');

    case instr.class is
      when BIT_OP =>
        instr.b := opc(9 downto 7);
        instr.f := opc(6 downto 0);
      when BYTE_OP =>
        instr.f := opc(6 downto 0);
        instr.d := opc(7);
      when LITERAL_OP | CONTROL_OP =>
        instr.k := opc(10 downto 0);
      when UNKNOWN =>
    end case;

    return instr;
  end function;

  pure function instruction_get_class(opc : std_logic_vector(13 downto 0))
    return instruction_class_t is
  begin
    -- RETURN (0x0008) and RETFIE (0x0009) have bits[13:12]="00"
    if opc = "00000000001000" then return LITERAL_OP; end if;
    if opc = "00000000001001" then return LITERAL_OP; end if;
    case opc(13 downto 12) is
      -- byte-oriented file-register operations
      when "00" =>
        return BYTE_OP;
      when "01" =>
        return BIT_OP;
      when "10" =>
        return CONTROL_OP;
      when "11" =>
        return LITERAL_OP;
      when others =>
        return UNKNOWN;
    end case;
  end function;

  pure function instruction_get_operation(
    opc         : std_logic_vector(13 downto 0);
    instr_class : instruction_class_t)
    return alu_op is
  begin
    case instr_class is

      when BYTE_OP =>
        case opc(11 downto 8) is
          when "0000" =>
            if opc(7) = '1' then return MOVWF;
            else return NOP;
            end if;
          when "0001" =>
            if opc(7) = '1' then return CLRF;
            else return CLRW;
            end if;
          when "0010" => return SUBWF;
          when "0011" => return DECF;
          when "0100" => return IORWF;
          when "0101" => return ANDWF;
          when "0110" => return XORWF;
          when "0111" => return ADDWF;
          when "1000" => return MOVF;
          when "1001" => return COMF;
          when "1010" => return INCF;
          when "1011" => return DECFSZ;
          when "1100" => return RRF;
          when "1101" => return RLF;
          when "1110" => return SWAPF;
          when "1111" => return INCFSZ;
          when others => return NOP;
        end case;

      when BIT_OP =>
        case opc(11 downto 10) is
          when "00"   => return BCF;
          when "01"   => return BSF;
          when "10"   => return BTFSC;
          when "11"   => return BTFSS;
          when others => return NOP;
        end case;

      when CONTROL_OP =>
        if opc(11) = '1' then return GOTO;
        else return CALL;
        end if;

      when LITERAL_OP =>
        -- fixed full-word patterns first (fall inside 00xx space)
        if opc(11 downto 0) = "000000001001" then return RETFIE;
        elsif opc(11 downto 0) = "000000001000" then return RETUR;
        -- elsif opc(11 downto 0) = "000000000011" then return SLEEP;
        -- elsif opc(11 downto 0) = "000000000100" then return CLRWDT;
        else
          case? opc(11 downto 8) is
            when "00--" => return MOVLW;
            when "01--" => return RETLW;
            when "1000" => return IORLW;
            when "1001" => return ANDLW;
            when "1010" => return XORLW;
            when "110-" => return SUBLW;
            when "111-" => return ADDLW;
            when others => return NOP;
          end case?;
        end if;

      when others => return NOP;

    end case;
  end function;

end decoder;