summaryrefslogtreecommitdiffstats
path: root/PIC/ALU.vhd
blob: b7c03440162d2ef64cd27d2e18dcf4d776786c90 (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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.alu_types.all;

entity alu is
  port (
    a, b : in std_logic_vector(7 downto 0);  -- inputs to ALU. In
                                             -- general here 'a' is
                                             -- a value from working
                                             -- register W and 'b'
                                             -- is from a file
                                             -- register.

    op : in alu_op;                     -- ALU operation which
    -- one to use

    bit_select : in std_logic_vector(2 downto 0);  -- this is the 'b' signal in
                                                   -- the datasheet.

    status_in : in std_logic_vector(2 downto 0);  -- status before ALU operation.

    status : out std_logic_vector(2 downto 0);  -- status after ALU operation.

    result : out std_logic_vector(7 downto 0);  -- result of the ALU
                                                -- operation. This is
                                                -- then either stored
                                                -- in file register f
                                                -- or work register W.


    skip : out std_logic                -- SKIP signal.

   -- the status register is of form:
   -- IRP  RP1  RP0  ~TO  ~PD  Z   DC  C
   --  7    6    5    4    3   2   1   0
   -- ALU only operates on 3 least significant bits.  
    );

end entity alu;

architecture rtl of alu is

  -- ternary op; returns x1 if cond is true, x2 otherwise.
  pure function sel(cond : boolean; x1, x2 : std_logic) return std_logic is
  begin
    if cond then return x1; else return x2; end if;
  end function;

  -- return status with the given flag set to val, all other bits same
  pure function set_flag(flag : alu_flag; val : std_logic; stat : std_logic_vector(2 downto 0)) return std_logic_vector is
    variable s_out : std_logic_vector(2 downto 0) := stat;
  begin
    case flag is
      when FLAG_Z  => s_out(2) := val;
      when FLAG_DC => s_out(1) := val;
      when FLAG_C  => s_out(0) := val;
    end case;
    return s_out;
  end function;

  -- Computes val +/- 1, drives result and either sets Z flag or skip signal.
  procedure inc_dec(
    val                : in  std_logic_vector(7 downto 0);
    inc                : in  boolean;   -- true = increment, false = decrement
    do_skip            : in  boolean;   -- true for *FSZ variants
    signal res         : out std_logic_vector(7 downto 0);
    signal stat        : out std_logic_vector(2 downto 0);
    signal do_skip_out : out std_logic;
    stat_in            : in  std_logic_vector(2 downto 0)
    ) is
    variable t : std_logic_vector(7 downto 0);
  begin
    if inc then
      t := std_logic_vector(unsigned(val) + 1);
    else
      t := std_logic_vector(unsigned(val) - 1);
    end if;
    res <= t;
    if do_skip then
      if unsigned(t) = 0 then
        do_skip_out <= '1';
      else
        do_skip_out <= '0';
      end if;
    else
      stat <= set_flag(FLAG_Z, sel(unsigned(t) = 0, '1', '0'), stat_in);
    end if;
  end procedure;

begin
  process(a, b, op, bit_select, status_in)
    variable tmp          : std_logic_vector(7 downto 0);
    variable tmp_extended : std_logic_vector(8 downto 0);

    variable tmp_status : std_logic_vector(2 downto 0);
  begin

    -- DEFAULT case (NOP)
    result <= (others => '0');          -- default: zero result
    skip   <= '0';                      -- default: no skip
    status <= status_in;                -- default: pass status thru

    case op is
      when NOP =>                       -- nothing


      when ADDWF | ADDLW =>
        -- addition
        tmp_extended := std_logic_vector(unsigned('0' & a) + unsigned('0' & b));

        -- DC
        tmp(4 downto 0) := std_logic_vector(('0' & unsigned(a(3 downto 0))) + ('0' & unsigned(b(3 downto 0))));

        -- flags
        tmp_status := set_flag(FLAG_Z, sel(unsigned(tmp_extended(7 downto 0)) = 0, '1', '0'), status_in);
        tmp_status := set_flag(FLAG_C, tmp_extended(8), tmp_status);
        tmp_status := set_flag(FLAG_DC, tmp(4), tmp_status);

        status <= tmp_status;
        result <= tmp_extended(7 downto 0);

      when SUBLW | SUBWF =>
        -- PIC16F84A: SUBWF = f - W, SUBLW = k - W  →  dest = b - a
        tmp_extended := std_logic_vector(unsigned('0' & b) - unsigned('0' & a));

        -- DC: borrow out of lower nibble
        tmp(4 downto 0) := std_logic_vector(('0' & unsigned(b(3 downto 0))) - ('0' & unsigned(a(3 downto 0))));

        tmp_status := set_flag(FLAG_Z, sel(unsigned(tmp_extended(7 downto 0)) = 0, '1', '0'), status_in);
        tmp_status := set_flag(FLAG_C, not tmp_extended(8), tmp_status);
        tmp_status := set_flag(FLAG_DC, not tmp(4), tmp_status);

        status <= tmp_status;
        result <= tmp_extended(7 downto 0);

      when IORLW | IORWF =>
        tmp    := a or b;
        result <= tmp;
        status <= set_flag(FLAG_Z, sel(unsigned(tmp) = 0, '1', '0'), status_in);

      when ANDWF | ANDLW =>
        tmp    := a and b;
        result <= tmp;
        status <= set_flag(FLAG_Z, sel(unsigned(tmp) = 0, '1', '0'), status_in);

      when XORLW | XORWF =>
        tmp    := a xor b;
        result <= tmp;
        status <= set_flag(FLAG_Z, sel(unsigned(tmp) = 0, '1', '0'), status_in);

      when COMF =>
        tmp    := not b;
        result <= tmp;
        status <= set_flag(FLAG_Z, sel(unsigned(tmp) = 0, '1', '0'), status_in);

      when DECF   => inc_dec(b, false, false, result, status, skip, status_in);
      when DECFSZ => inc_dec(b, false, true, result, status, skip, status_in);
      when INCF   => inc_dec(b, true, false, result, status, skip, status_in);
      when INCFSZ => inc_dec(b, true, true, result, status, skip, status_in);

      when RLF =>
        tmp       := std_logic_vector(shift_left(unsigned(b), 1));
        tmp(0)    := status_in(0);
        status(0) <= b(7);
        result    <= tmp;

      when RRF =>
        tmp       := std_logic_vector(shift_right(unsigned(b), 1));
        tmp(7)    := status_in(0);
        status(0) <= b(0);
        result    <= tmp;


        
      when BCF =>
        result                                   <= b;
        result(to_integer(unsigned(bit_select))) <= '0';

      when BTFSC =>
        skip <= not b(to_integer(unsigned(bit_select)));

      when BSF =>
        result                                   <= b;
        result(to_integer(unsigned(bit_select))) <= '1';

      when BTFSS =>
        skip <= b(to_integer(unsigned(bit_select)));

      when SWAPF =>
        tmp(7 downto 4) := b(3 downto 0);
        tmp(3 downto 0) := b(7 downto 4);
        result          <= tmp;


      when CLRF | CLRW =>
        result <= (others => '0');
        status <= set_flag(FLAG_Z, '1', status_in);

      when MOVF =>
        result <= b;
        status <= set_flag(FLAG_Z, sel(unsigned(b) = 0, '1', '0'), status_in);
      when MOVWF => result <= a;
      when MOVLW => result <= b;




      when CALL  => null;
      when GOTO  => null;
      when RETLW => result <= b;
      when RETUR => null;

    end case;
  end process;
end architecture rtl;