237 lines
5.9 KiB
VHDL
237 lines
5.9 KiB
VHDL
--------------------------------------------------------------------------
|
|
-- Project: JCPU, a portable 8-bit RISC CPU written in VHDL
|
|
-- This file: The arithmetic logic unit
|
|
--
|
|
-- Copyright (C) 2007 J. Ahrensfeld
|
|
--
|
|
-- This program is free software: you can redistribute it and/or modify
|
|
-- it under the terms of the GNU General Public License as published by
|
|
-- the Free Software Foundation, either version 3 of the License, or
|
|
-- (at your option) any later version.
|
|
--
|
|
-- This program is distributed in the hope that it will be useful,
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
-- GNU General Public License for more details.
|
|
--
|
|
-- You should have received a copy of the GNU General Public License
|
|
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
--
|
|
-- For questions and ideas, please contact the author at jens@jayfield.org
|
|
--
|
|
--------------------------------------------------------------------------
|
|
|
|
library IEEE;
|
|
use IEEE.STD_LOGIC_1164.ALL;
|
|
use IEEE.numeric_std.ALL;
|
|
|
|
use work.cpu_pkg.all;
|
|
|
|
entity alu is
|
|
Generic (
|
|
data_width : integer := 8
|
|
);
|
|
Port (
|
|
rst : in STD_LOGIC;
|
|
clk : in STD_LOGIC;
|
|
op_en : in STD_LOGIC;
|
|
cy_in : in STD_LOGIC;
|
|
op1_in : in unsigned (data_width-1 downto 0);
|
|
op2_in : in unsigned (data_width-1 downto 0);
|
|
opsel : in alu_op_t;
|
|
res_out : out unsigned (data_width-1 downto 0);
|
|
stat_load : in STD_LOGIC;
|
|
stat_in : in alu_status_t;
|
|
stat_out : out alu_status_t
|
|
);
|
|
end alu;
|
|
|
|
architecture Behavioral of alu is
|
|
|
|
signal sum_cy, sh_cy, sum_cy_in, sh_cy_in, left_shift, add_subn : STD_LOGIC;
|
|
signal result, sum_res, lf_res, sh_res, res_reg : unsigned (data_width-1 downto 0);
|
|
signal status, stat_reg : alu_status_t;
|
|
|
|
--------------------------------------------------------------------------
|
|
function test_zero(arg : unsigned) return STD_LOGIC is
|
|
variable result : STD_LOGIC;
|
|
begin
|
|
result := '0';
|
|
for i in arg'range loop
|
|
result := result or To_X01(arg(i));
|
|
end loop;
|
|
|
|
return not result;
|
|
end test_zero;
|
|
|
|
--------------------------------------------------------------------------
|
|
begin
|
|
|
|
--------------------------------------------------------------------------
|
|
proc_logic_func:
|
|
process (op1_in, op2_in, opsel)
|
|
begin
|
|
|
|
case opsel is
|
|
when op1_and_op2 =>
|
|
lf_res <= op1_in and op2_in;
|
|
when op1_or_op2 =>
|
|
lf_res <= op1_in or op2_in;
|
|
when op1_xor_op2 =>
|
|
lf_res <= op1_in xor op2_in;
|
|
when others =>
|
|
lf_res <= (others => '-');
|
|
end case;
|
|
|
|
end process;
|
|
|
|
--------------------------------------------------------------------------
|
|
proc_shift_func:
|
|
process (op1_in, left_shift, sh_cy_in)
|
|
begin
|
|
if left_shift = '1' then
|
|
sh_res <= op1_in(op1_in'left-1 downto op1_in'right) & sh_cy_in;
|
|
sh_cy <= op1_in(op1_in'left);
|
|
else
|
|
sh_res <= sh_cy_in & op1_in(op1_in'left downto op1_in'right+1);
|
|
sh_cy <= op1_in(op1_in'right);
|
|
end if;
|
|
|
|
end process;
|
|
|
|
--------------------------------------------------------------------------
|
|
proc_alu_out:
|
|
process(opsel, op1_in, op2_in, res_reg, stat_reg, lf_res, sum_res, sh_res, sum_cy, sh_cy, cy_in)
|
|
|
|
begin
|
|
|
|
case opsel is
|
|
|
|
when op1_add_op2 | op1_sub_op2 | op1_addc_op2 | op1_subc_op2 =>
|
|
status.zero <= test_zero(res_reg);
|
|
status.carry <= sum_cy;
|
|
result <= sum_res;
|
|
when op1_and_op2 | op1_or_op2 | op1_xor_op2 =>
|
|
status.zero <= test_zero(res_reg);
|
|
status.carry <= stat_reg.carry;
|
|
result <= lf_res;
|
|
when shl_op | shr_op | rol_op | ror_op | rolc_op | rorc_op =>
|
|
status.zero <= test_zero(res_reg);
|
|
status.carry <= sh_cy;
|
|
result <= sh_res;
|
|
when swap_op =>
|
|
result <= op1_in(op1_in'left-op1_in'length/2 downto op1_in'right) & op1_in(op1_in'left downto op1_in'right+op1_in'length/2);
|
|
status <= stat_reg;
|
|
when others =>
|
|
result <= op2_in;
|
|
status.zero <= stat_reg.zero;
|
|
status.carry <= stat_reg.carry or cy_in;
|
|
end case;
|
|
|
|
end process;
|
|
|
|
--------------------------------------------------------------------------
|
|
proc_alu_in:
|
|
process(opsel, op1_in, stat_reg)
|
|
|
|
begin
|
|
|
|
add_subn <= '-';
|
|
sum_cy_in <= '-';
|
|
sh_cy_in <= '-';
|
|
left_shift <= '-';
|
|
|
|
case opsel is
|
|
|
|
when op1_add_op2 =>
|
|
sum_cy_in <= '0';
|
|
add_subn <= '1';
|
|
|
|
when op1_sub_op2 =>
|
|
sum_cy_in <= '0';
|
|
add_subn <= '0';
|
|
|
|
when op1_addc_op2 =>
|
|
sum_cy_in <= stat_reg.carry;
|
|
add_subn <= '1';
|
|
|
|
when op1_subc_op2 =>
|
|
sum_cy_in <= stat_reg.carry;
|
|
add_subn <= '0';
|
|
|
|
when shl_op =>
|
|
sh_cy_in <= '0';
|
|
left_shift <= '1';
|
|
|
|
when shr_op =>
|
|
sh_cy_in <= '0';
|
|
left_shift <= '0';
|
|
|
|
when rol_op =>
|
|
sh_cy_in <= op1_in(op1_in'left);
|
|
left_shift <= '1';
|
|
|
|
when ror_op =>
|
|
sh_cy_in <= op1_in(op1_in'right);
|
|
left_shift <= '0';
|
|
|
|
when rolc_op =>
|
|
sh_cy_in <= stat_reg.carry;
|
|
left_shift <= '1';
|
|
|
|
when rorc_op =>
|
|
sh_cy_in <= stat_reg.carry;
|
|
left_shift <= '0';
|
|
|
|
when others => null;
|
|
|
|
end case;
|
|
|
|
end process;
|
|
|
|
--------------------------------------------------------------------------
|
|
proc_res_reg:
|
|
process(clk, rst, op_en, stat_load, res_reg, stat_reg)
|
|
begin
|
|
if rst = '1' then
|
|
res_reg <= (others => '0');
|
|
stat_reg <= (others => '0');
|
|
elsif rising_edge(clk) then
|
|
if op_en = '1' then
|
|
res_reg <= result;
|
|
stat_reg <= status;
|
|
elsif stat_load = '1' then
|
|
stat_reg <= stat_in;
|
|
end if;
|
|
end if;
|
|
res_out <= res_reg;
|
|
stat_out <= stat_reg;
|
|
|
|
end process;
|
|
|
|
--------------------------------------------------------------------------
|
|
alu_addsub:
|
|
process(op1_in, op2_in, sum_cy_in, add_subn)
|
|
variable sum : unsigned(data_width+1 downto 0);
|
|
variable op1, op2 : unsigned(data_width+1 downto 0);
|
|
begin
|
|
if add_subn = '1' then
|
|
op1 := '0' & op1_in & '1';
|
|
op2 := '0' & op2_in & sum_cy_in;
|
|
sum := op1 + op2;
|
|
else
|
|
op1 := '0' & op1_in & not sum_cy_in;
|
|
op2 := '0' & op2_in & '1';
|
|
sum := op1 - op2;
|
|
end if;
|
|
|
|
-- Form sum + carry
|
|
sum_res <= sum(data_width downto 1);
|
|
sum_cy <= sum(sum'left);
|
|
|
|
end process;
|
|
|
|
--------------------------------------------------------------------------
|
|
end Behavioral;
|
|
|