| Line 1... |
Line 1... |
-- Copyright (c)2013 Jeremy Seth Henry
|
-- Copyright (c)2006, 2015, 2020 Jeremy Seth Henry
|
-- All rights reserved.
|
-- All rights reserved.
|
--
|
--
|
-- Redistribution and use in source and binary forms, with or without
|
-- Redistribution and use in source and binary forms, with or without
|
-- modification, are permitted provided that the following conditions are met:
|
-- modification, are permitted provided that the following conditions are met:
|
-- * Redistributions of source code must retain the above copyright
|
-- * Redistributions of source code must retain the above copyright
|
| Line 27... |
Line 27... |
--
|
--
|
-- Notes : All output registers are updated by writing an instruction to
|
-- Notes : All output registers are updated by writing an instruction to
|
-- : offset 0x1F.
|
-- : offset 0x1F.
|
-- : The math unit is busy when the MSB of the status
|
-- : The math unit is busy when the MSB of the status
|
-- : register is high, and done/ready when it reads low.
|
-- : register is high, and done/ready when it reads low.
|
-- : Almost Equal checks to see if Addend 1 is no more, or less
|
-- : Almost Equal checks to see if Addend 1 is no more, or less,
|
-- : addend 2 within the specified tolerance. For example,
|
-- : addend 2 within the specified tolerance. For example,
|
-- : addend_1 = 2 is almost equal to addend_2 = -1 with a
|
-- : addend_1 = 2 is almost equal to addend_2 = -1 with a
|
-- : tolerance of 3, but not a tolerance of 1. Actual function is
|
-- : tolerance of 3, but not a tolerance of 1. Actual function is
|
-- : AE = '1' when (A1 <= A2 + T) and (A1 >= A2 - T) else '0'
|
-- : AE = '1' when (A1 <= A2 + T) and (A1 >= A2 - T) else '0'
|
-- : This is an inherently signed function.
|
-- : This is an inherently signed function.
|
-- : Signed Overflow/Underflow is logically equivalent to
|
-- : Signed Overflow/Underflow is logically equivalent to
|
-- : S_Sum/Dif(16) xor S_Sum/Dif(15), since the apparent result
|
-- : S_Sum/Dif(16) xor S_Sum/Dif(15), since the apparent result
|
-- : changes sign while the internal sign bit does not. For example
|
-- : changes sign while the internal sign bit doesn't. For example
|
-- : |8000| will result in (-)8000 due to the way the internal
|
-- : |8000| will result in (-)8000 due to the way the internal
|
-- : logic handles sign extension. Thus, the O bit should be
|
-- : logic handles sign extension. Thus, the O bit should be
|
-- : checked when performing signed math
|
-- : checked when performing signed math
|
-- : Decimal Adjust converts the contents of a register into its BCD
|
-- : Decimal Adjust converts the contents of a register into its BCD
|
-- : equivalent. This can be used to get the base 10 representation
|
-- : equivalent. This can be used to get the base 10 representation
|
-- : of a value for conversion to ASCII. There are two variants,
|
-- : of a value for conversion to ASCII. There are two variants,
|
-- : Byte and Word. Note that conversion times are fairly long,
|
-- : Byte and Word. Note that conversion times are fairly long,
|
-- : since we are repeatedly issuing division commands, but it is
|
-- : since we are repeatedly issuing division commands, but it is
|
-- : still faster than software emulation.
|
-- : still faster than software emulation.
|
-- : The Byte conversion only operates on the lower 8 bits, and sets
|
-- : The Byte conversion only operates on the lower 8 bits, and
|
-- : the Z and N flags. The N flag is only set when SDAB is used
|
-- : sets the Z and N flags. The N flag is only set when SDAB is
|
-- : and the signed value of the register is negative. The O bit is
|
-- : used and the signed value of the register is negative. The O
|
-- : set if the upper byte of the register is non-zero, but does
|
-- : bit is set if the upper byte of the register is non-zero, but
|
-- : not actually result in an calculation error.
|
-- : does not actually result in an calculation error.
|
-- : Examples:
|
-- : Examples:
|
-- : UDAB 0x00FE -> 0x0254, Flags -> 0x0
|
-- : UDAB 0x00FE -> 0x0254, Flags -> 0x0
|
-- : SDAB 0x00FE -> 0x0002, Flags -> 0x4
|
-- : SDAB 0x00FE -> 0x0002, Flags -> 0x4
|
-- : The Word conversion uses the entire 16-bit word, and uses the
|
-- : The Word conversion uses the entire 16-bit word, and uses the
|
-- : Flags register to hold the Tens of Thousands place. Note that
|
-- : Flags register to hold the Tens of Thousands place. Note that
|
-- : the N flag is still used for signed conversions, while it may
|
-- : the N flag is still used for signed conversions, while it may
|
-- : be used as a data bit for unsigned conversions.
|
-- : be used as a data bit for unsigned conversions.
|
-- : Examples:
|
-- : Examples:
|
-- : UDAW 0xD431 -> 0x4321, Flags -> 0x5 ('0', MSB, MSB-1, MSB-2)
|
-- : UDAW 0xD431 -> 0x4321, Flags -> 0x5 ('0', MSB, MSB-1, MSB-2)
|
-- : SDAW 0xD431 -> 0x1216, Flags -> 0x5 ('0', N , MSB , MSB-1)
|
-- : SDAW 0xD431 -> 0x1216, Flags -> 0x5 ('0', N , MSB , MSB-1)
|
|
--
|
-- Register Map:
|
-- Register Map:
|
-- Offset Bitfield Description Read/Write
|
-- Offset Bitfield Description Read/Write
|
-- 0x00 AAAAAAAA Register 0 ( 7:0) (RW)
|
-- 0x00 AAAAAAAA Register 0 ( 7:0) (RW)
|
-- 0x01 AAAAAAAA Register 0 (15:8) (RW)
|
-- 0x01 AAAAAAAA Register 0 (15:8) (RW)
|
-- 0x02 AAAAAAAA Register 1 ( 7:0) (RW)
|
-- 0x02 AAAAAAAA Register 1 ( 7:0) (RW)
|
| Line 93... |
Line 93... |
-- 0x19 -------- Reserved (--)
|
-- 0x19 -------- Reserved (--)
|
-- 0x1A -------- Reserved (--)
|
-- 0x1A -------- Reserved (--)
|
-- 0x1B -------- Reserved (--)
|
-- 0x1B -------- Reserved (--)
|
-- 0x1C AAAAAAAA Tolerance ( 7:0) (RW)
|
-- 0x1C AAAAAAAA Tolerance ( 7:0) (RW)
|
-- 0x1D AAAAAAAA Tolerance (15:8) (RW)
|
-- 0x1D AAAAAAAA Tolerance (15:8) (RW)
|
-- 0x1E E---DCBA Status & Flags (RW)
|
-- 0x1E BBBBBAAA Instruction Register (RW)
|
|
-- A = Operand (register select)
|
|
-- B = Opcode (instruction select)
|
|
-- 0x1F E---DCBA Status & Flags (RW)
|
-- A = Zero Flag
|
-- A = Zero Flag
|
-- B = Carry Flag
|
-- B = Carry Flag
|
-- C = Negative Flag
|
-- C = Negative Flag
|
-- D = Overflow / Error Flag
|
-- D = Overflow / Error Flag
|
-- E = Busy Flag (1 = busy, 0 = idle)
|
-- E = Busy Flag (1 = busy, 0 = idle)
|
-- 0x1F BBBBBAAA Instruction Register (RW)
|
|
-- A = Operand (register select)
|
|
-- B = Opcode (instruction select)
|
|
--
|
--
|
-- Instruction Map:
|
-- Instruction Map:
|
-- OP_T0X "0000 0xxx" : Transfer R0 to Rx R0 -> Rx (Sets Z,N)
|
-- OP_T0X "0000 0xxx" : Transfer R0 to Rx R0 -> Rx (Sets Z,N)
|
-- OP_TX0 "0000 1xxx" : Transfer Rx to R0 Rx -> R0 (Sets Z,N)
|
-- OP_TX0 "0000 1xxx" : Transfer Rx to R0 Rx -> R0 (Sets Z,N)
|
-- OP_CLR "0001 0xxx" : Set Rx to 0 0x00 -> Rx (Sets Z,N)
|
-- OP_CLR "0001 0xxx" : Set Rx to 0 0x00 -> Rx (Sets Z,N)
|
| Line 145... |
Line 145... |
-- OP_BINV "1101 1xxx" : Bitwise logical NOT #Rx -> Rx
|
-- OP_BINV "1101 1xxx" : Bitwise logical NOT #Rx -> Rx
|
-- OP_BSFL "1110 0xxx" : Logical Shift Left Rx<<1,0 -> Rx
|
-- OP_BSFL "1110 0xxx" : Logical Shift Left Rx<<1,0 -> Rx
|
-- OP_BROL "1110 1xxx" : Logical Rotate Left Rx<<1,C -> Rx,C
|
-- OP_BROL "1110 1xxx" : Logical Rotate Left Rx<<1,C -> Rx,C
|
-- OP_BSFR "1111 0xxx" : Logical Shift Right 0,Rx>>1 -> Rx
|
-- OP_BSFR "1111 0xxx" : Logical Shift Right 0,Rx>>1 -> Rx
|
-- OP_BROR "1111 1xxx" : Logical Rotate Right C,Rx>>1 -> Rx,C
|
-- OP_BROR "1111 1xxx" : Logical Rotate Right C,Rx>>1 -> Rx,C
|
|
--
|
|
-- Revision History
|
|
-- Author Date Change
|
|
------------------ -------- --------------------------------------------------
|
|
-- Seth Henry 07/19/06 Design Start
|
|
-- Seth Henry 03/13/15 Added "Almost Equal" instruction
|
|
-- Seth Henry 12/19/19 Renamed to o8_alu16 to fit "theme"
|
|
|
library ieee;
|
library ieee;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_arith.all;
|
use ieee.std_logic_arith.all;
|
use ieee.std_logic_unsigned.all;
|
use ieee.std_logic_unsigned.all;
|
| Line 234... |
Line 241... |
constant OP_BROL : std_logic_vector(4 downto 0) := "11101";
|
constant OP_BROL : std_logic_vector(4 downto 0) := "11101";
|
constant OP_BSFR : std_logic_vector(4 downto 0) := "11110";
|
constant OP_BSFR : std_logic_vector(4 downto 0) := "11110";
|
constant OP_BROR : std_logic_vector(4 downto 0) := "11111";
|
constant OP_BROR : std_logic_vector(4 downto 0) := "11111";
|
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
|
|
constant User_Addr : std_logic_vector(15 downto 5):= Address(15 downto 5);
|
constant User_Addr : std_logic_vector(15 downto 5):=
|
|
Address(15 downto 5);
|
alias Comp_Addr is Bus_Address(15 downto 5);
|
alias Comp_Addr is Bus_Address(15 downto 5);
|
signal Reg_Addr : std_logic_vector(4 downto 0);
|
signal Reg_Addr : std_logic_vector(4 downto 0);
|
|
|
signal Addr_Match : std_logic;
|
signal Addr_Match : std_logic;
|
signal Wr_En : std_logic;
|
signal Wr_En : std_logic;
|
| Line 306... |
Line 314... |
signal s_prod : signed(33 downto 0);
|
signal s_prod : signed(33 downto 0);
|
|
|
signal IDIV_Start : std_logic;
|
signal IDIV_Start : std_logic;
|
signal IDIV_Busy : std_logic;
|
signal IDIV_Busy : std_logic;
|
|
|
constant N : integer := 16; -- Width of Operands
|
constant DIV_WIDTH : integer := 16; -- Width of Operands
|
|
|
signal q : std_logic_vector(N*2-1 downto 0);
|
signal q : std_logic_vector(DIV_WIDTH*2-1 downto 0);
|
signal diff : std_logic_vector(N downto 0);
|
signal diff : std_logic_vector(DIV_WIDTH downto 0);
|
signal count : integer range 0 to N + 1;
|
signal count : integer range 0 to DIV_WIDTH + 1;
|
|
|
signal Quotient_i : std_logic_vector(15 downto 0);
|
signal Quotient_i : std_logic_vector(15 downto 0);
|
signal Quotient : std_logic_vector(15 downto 0);
|
signal Quotient : std_logic_vector(15 downto 0);
|
|
|
signal Remainder_i : std_logic_vector(15 downto 0);
|
signal Remainder_i : std_logic_vector(15 downto 0);
|
| Line 344... |
Line 352... |
|
|
-- Combinational logic for the Decimal Adjust logic
|
-- Combinational logic for the Decimal Adjust logic
|
DAA_result <= DAA_p4 & DAA_p3 & DAA_p2 & DAA_p1 & DAA_p0;
|
DAA_result <= DAA_p4 & DAA_p3 & DAA_p2 & DAA_p1 & DAA_p0;
|
|
|
-- Combinational logic for the division logic
|
-- Combinational logic for the division logic
|
diff <= ('0' & Q(N*2-2 downto N-1)) - ('0' & Divisor);
|
diff <= ('0' & Q(DIV_WIDTH*2-2 downto DIV_WIDTH-1)) -
|
Quotient_i <= q(N-1 downto 0);
|
('0' & Divisor);
|
Remainder_i <= q(N*2-1 downto N);
|
Quotient_i <= q(DIV_WIDTH-1 downto 0);
|
|
Remainder_i <= q(DIV_WIDTH*2-1 downto DIV_WIDTH);
|
|
|
ALU_proc: process( Clock, Reset )
|
ALU_proc: process( Clock, Reset )
|
variable Reg_Sel : integer;
|
variable Reg_Sel : integer;
|
variable Oper_Sel : integer;
|
variable Oper_Sel : integer;
|
begin
|
begin
|
if( Reset = Reset_Level )then
|
if( Reset = Reset_Level )then
|
Wr_En <= '0';
|
Wr_En <= '0';
|
Wr_Data_q <= (others => '0');
|
Wr_Data_q <= (others => '0');
|
Rd_En <= '0';
|
Rd_En <= '0';
|
Rd_Data <= (others => '0');
|
Rd_Data <= OPEN8_NULLBUS;
|
Reg_Addr <= (others => '0');
|
Reg_Addr <= (others => '0');
|
Opcode <= (others => '0');
|
Opcode <= (others => '0');
|
Operand_Sel <= (others => '0');
|
Operand_Sel <= (others => '0');
|
Tolerance <= (others => '0');
|
Tolerance <= (others => '0');
|
Start <= '0';
|
Start <= '0';
|
| Line 387... |
Line 396... |
DAA_p4 <= (others => '0');
|
DAA_p4 <= (others => '0');
|
DAA_p3 <= (others => '0');
|
DAA_p3 <= (others => '0');
|
DAA_p2 <= (others => '0');
|
DAA_p2 <= (others => '0');
|
IDIV_Start <= '0';
|
IDIV_Start <= '0';
|
q <= (others => '0');
|
q <= (others => '0');
|
count <= N;
|
count <= DIV_WIDTH;
|
IDIV_Busy <= '0';
|
IDIV_Busy <= '0';
|
elsif( rising_edge(Clock) )then
|
elsif( rising_edge(Clock) )then
|
-- For convenience, convert these to integers and assign them to
|
-- For convenience, convert these to integers and assign them to
|
-- variables
|
-- variables
|
Reg_Sel := conv_integer(Reg_Addr(3 downto 1));
|
Reg_Sel := conv_integer(Reg_Addr(3 downto 1));
|
| Line 418... |
Line 427... |
Tolerance(7 downto 0) <= Wr_Data_q;
|
Tolerance(7 downto 0) <= Wr_Data_q;
|
|
|
when "11101" => -- 0x1D -> Tolerance.u
|
when "11101" => -- 0x1D -> Tolerance.u
|
Tolerance(15 downto 8) <= Wr_Data_q;
|
Tolerance(15 downto 8) <= Wr_Data_q;
|
|
|
when "11110" => -- 0x1E -> Status register
|
when "11110" => -- 0x1E -> Opcode register
|
null;
|
|
|
|
when "11111" => -- 0x1F -> Control register
|
|
Start <= '1';
|
|
Opcode <= Wr_Data_q(7 downto 3);
|
Opcode <= Wr_Data_q(7 downto 3);
|
Operand_Sel <= Wr_Data_q(2 downto 0);
|
Operand_Sel <= Wr_Data_q(2 downto 0);
|
|
|
|
when "11111" => -- 0x1F -> Status/Start register
|
|
Start <= '1';
|
|
|
when others => null;
|
when others => null;
|
end case;
|
end case;
|
end if;
|
end if;
|
|
|
Rd_Data <= (others => '0');
|
Rd_Data <= OPEN8_NULLBUS;
|
Rd_En <= Addr_Match and Rd_Enable;
|
Rd_En <= Addr_Match and Rd_Enable;
|
|
|
if( Rd_En = '1' )then
|
if( Rd_En = '1' )then
|
case( Reg_Addr )is
|
case( Reg_Addr )is
|
when "00000" | "00010" | "00100" | "00110" |
|
when "00000" | "00010" | "00100" | "00110" |
|
| Line 445... |
Line 453... |
Rd_Data <= regfile(Reg_Sel)(15 downto 8);
|
Rd_Data <= regfile(Reg_Sel)(15 downto 8);
|
when "11100" => -- 0x1C -> Tolerance.l
|
when "11100" => -- 0x1C -> Tolerance.l
|
Rd_Data <= Tolerance(7 downto 0);
|
Rd_Data <= Tolerance(7 downto 0);
|
when "11101" => -- 0x1D -> Tolerance.u
|
when "11101" => -- 0x1D -> Tolerance.u
|
Rd_Data <= Tolerance(15 downto 8);
|
Rd_Data <= Tolerance(15 downto 8);
|
when "11110" => -- 0x1E -> Flags & Status register
|
when "11110" => -- 0x1E -> Opcode register
|
Rd_Data <= Busy & "000" & Flags;
|
|
when "11111" => -- 0x1F -> Control register
|
|
Rd_Data <= Opcode & Operand_Sel;
|
Rd_Data <= Opcode & Operand_Sel;
|
|
when "11111" => -- 0x1F -> Flags & Status register
|
|
Rd_Data <= Busy & "000" & Flags;
|
|
|
when others => null;
|
when others => null;
|
end case;
|
end case;
|
end if;
|
end if;
|
|
|
Busy <= '1';
|
Busy <= '1';
|
| Line 462... |
Line 471... |
Busy <= '0';
|
Busy <= '0';
|
if( Start = '1' )then
|
if( Start = '1' )then
|
alu_ctrl <= LOAD;
|
alu_ctrl <= LOAD;
|
end if;
|
end if;
|
|
|
-- Load the operands from the register file. We also check for specific
|
-- Load the operands from the reg file. We also check for specific
|
-- opcodes to set the DAA mode (signed vs unsigned). This is the only
|
-- opcodes to set the DAA mode (signed vs unsigned). This is the only
|
-- place where we READ the register file outside of the bus interface
|
-- place where we READ the register file outside of the bus interface
|
when LOAD =>
|
when LOAD =>
|
Operand_1 <= regfile(0);
|
Operand_1 <= regfile(0);
|
Operand_2 <= regfile(Oper_Sel);
|
Operand_2 <= regfile(Oper_Sel);
|
| Line 513... |
Line 522... |
|
|
when OP_SADD =>
|
when OP_SADD =>
|
s_accum <= S_Addend_1 + S_Addend_2;
|
s_accum <= S_Addend_1 + S_Addend_2;
|
|
|
when OP_UADD =>
|
when OP_UADD =>
|
u_accum <= ('0' & Operand_1) + ('0' & Operand_2);
|
u_accum <= ('0' & Operand_1) +
|
|
('0' & Operand_2);
|
|
|
when OP_UADC =>
|
when OP_UADC =>
|
u_accum <= ('0' & Operand_1) + ('0' & Operand_2) + Flags(FLAG_C);
|
u_accum <= ('0' & Operand_1) +
|
|
('0' & Operand_2) +
|
|
Flags(FLAG_C);
|
|
|
when OP_SSUB | OP_SCMP =>
|
when OP_SSUB | OP_SCMP =>
|
s_accum <= S_Addend_1 - S_Addend_2;
|
s_accum <= S_Addend_1 - S_Addend_2;
|
|
|
when OP_USUB | OP_UCMP =>
|
when OP_USUB | OP_UCMP =>
|
u_accum <= ('0' & U_Addend_1) - ('0' & U_Addend_2);
|
u_accum <= ('0' & U_Addend_1) -
|
|
('0' & U_Addend_2);
|
|
|
when OP_USBC =>
|
when OP_USBC =>
|
u_accum <= ('0' & U_Addend_1) - ('0' & U_Addend_2) - Flags(FLAG_C);
|
u_accum <= ('0' & U_Addend_1) -
|
|
('0' & U_Addend_2) -
|
|
Flags(FLAG_C);
|
|
|
when OP_ACMP =>
|
when OP_ACMP =>
|
-- Perform the function
|
-- Perform the function
|
-- AE = '1' when (A1 <= A2 + T) and (A1 >= A2 - T) else '0'
|
-- AE = '1' when (A1 <= A2 + T) and (A1 >= A2 - T) else '0'
|
Almost_Equal <= '0';
|
Almost_Equal <= '0';
|
| Line 775... |
Line 790... |
|
|
IDIV_Busy <= '0';
|
IDIV_Busy <= '0';
|
if( IDIV_Start = '1' )then
|
if( IDIV_Start = '1' )then
|
IDIV_Busy <= '1';
|
IDIV_Busy <= '1';
|
count <= 0;
|
count <= 0;
|
q <= conv_std_logic_vector(0,N) & Dividend;
|
q <= conv_std_logic_vector(0,DIV_WIDTH) & Dividend;
|
elsif( count < N )then
|
elsif( count < DIV_WIDTH )then
|
IDIV_Busy <= '1';
|
IDIV_Busy <= '1';
|
count <= count + 1;
|
count <= count + 1;
|
q <= diff(N-1 downto 0) & q(N-2 downto 0) & '1';
|
q <= diff(DIV_WIDTH-1 downto 0) &
|
if( diff(N) = '1' )then
|
q(DIV_WIDTH-2 downto 0) &
|
q <= q(N*2-2 downto 0) & '0';
|
'1';
|
|
if( diff(DIV_WIDTH) = '1' )then
|
|
q <= q(DIV_WIDTH*2-2 downto 0) & '0';
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
-- Fire on the falling edge of Busy
|
-- Fire on the falling edge of Busy
|
Busy_q <= Busy;
|
Busy_q <= Busy;
|