-- AMD2901's behavioral description 
--
-- Connector's declaration
--
ENTITY amd IS
PORT (
       ck        : IN BIT;
       fonc      : IN BIT;
       test      : IN BIT;
       scin      : IN BIT;
       scout     : OUT BIT;
       i         : IN BIT_VECTOR (8 DOWNTO 0);
       a         : IN BIT_VECTOR (3 DOWNTO 0);
       b         : IN BIT_VECTOR (3 DOWNTO 0);
       d         : IN BIT_VECTOR (3 DOWNTO 0);
       noe       : IN BIT;
       r0        : INOUT MUX_BIT bus;
       r3        : INOUT MUX_BIT bus;
       q0        : INOUT MUX_BIT bus;
       q3        : INOUT MUX_BIT bus;
       ovr       : OUT BIT;
       zero      : OUT BIT;
       signe     : OUT BIT;
       np        : OUT BIT;
       ng        : OUT BIT;
       cin       : IN BIT;
       cout      : OUT BIT;
       y         : OUT MUX_VECTOR (3 DOWNTO 0) bus;
       vdd       : IN BIT;
       vss       : IN BIT;
       vddp      : IN BIT;
       vssp      : IN BIT
     );
END amd;
--
ARCHITECTURE data_flow OF amd IS
--
-- Internal registers
--
--
SIGNAL accu      : REG_VECTOR (3 DOWNTO 0) register;
SIGNAL ram0      : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram1      : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram2      : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram3      : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram4      : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram5      : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram6      : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram7      : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram8      : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram9      : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram10     : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram11     : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram12     : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram13     : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram14     : REG_VECTOR (3 DOWNTO 0) register; 
SIGNAL ram15     : REG_VECTOR (3 DOWNTO 0) register; 
--
-- Internal signals
--
--
SIGNAL r         : BIT_VECTOR (3 DOWNTO 0);
SIGNAL s         : BIT_VECTOR (3 DOWNTO 0);
SIGNAL alu_out   : BIT_VECTOR (3 DOWNTO 0);
SIGNAL sumrs     : BIT_VECTOR (3 DOWNTO 0);
SIGNAL difrs     : BIT_VECTOR (3 DOWNTO 0);
SIGNAL difsr     : BIT_VECTOR (3 DOWNTO 0);
SIGNAL c_sumrs   : BIT_VECTOR (3 DOWNTO 0);
SIGNAL c_difrs   : BIT_VECTOR (3 DOWNTO 0);
SIGNAL c_difsr   : BIT_VECTOR (3 DOWNTO 0);
SIGNAL waccu     : BIT;
SIGNAL fonc_mode : BIT;
SIGNAL test_mode : BIT;
sIGNAL shift_n   : BIT;
SIGNAL shift_l   : BIT;
SIGNAL shift_r   : BIT;
SIGNAL sh_acc    : BIT_VECTOR (3 DOWNTO 0);
SIGNAL sh_ram    : BIT_VECTOR (3 DOWNTO 0);
SIGNAL wram      : BIT;
SIGNAL wck0      : BIT ;
SIGNAL wck1      : BIT ;
SIGNAL wck2      : BIT ;
SIGNAL wck3      : BIT ;
SIGNAL wck4      : BIT ;
SIGNAL wck5      : BIT ;
SIGNAL wck6      : BIT ;
SIGNAL wck7      : BIT ;
SIGNAL wck8      : BIT ;
SIGNAL wck9      : BIT ;
SIGNAL wck10     : BIT ;
SIGNAL wck11     : BIT ;
SIGNAL wck12     : BIT ;
SIGNAL wck13     : BIT ;
SIGNAL wck14     : BIT ;
SIGNAL wck15     : BIT ;
SIGNAL wckaccu   : BIT ;
SIGNAL accu_in   : BIT_VECTOR ( 3 DOWNTO 0 ) ;
SIGNAL ra        : BIT_VECTOR ( 3 DOWNTO 0 ) ;
SIGNAL rb        : BIT_VECTOR ( 3 DOWNTO 0 ) ;
--
BEGIN
--
--
-- ALU-INPUT MULTIPLEXER 
--
WITH i(2 DOWNTO 0) SELECT
     s <= rb       WHEN "100",
          rb       WHEN "101",
          ra       WHEN "001",
          ra       WHEN "011",
          NOT accu WHEN "000",
          NOT accu WHEN "010",
          NOT accu WHEN "110",
          "0000"   WHEN "111";
--
--
WITH i(2 DOWNTO 0) SELECT
     r <= ra       WHEN "000",
          ra       WHEN "001",
          "0000"   WHEN "010",
          "0000"   WHEN "011",
          "0000"   WHEN "100",
          d        WHEN "101", 
          d WHEN "110", 
          d WHEN "111"; 
--
--
--
--
--  THREE-STATE OUTPUT MULTIPLEXER 
--
muxs: BLOCK (noe='0')
BEGIN
WITH i(8 DOWNTO 6) SELECT
     y <= GUARDED ra WHEN "010",
          alu_out WHEN "000",
          alu_out WHEN "001",
          alu_out WHEN "011",
          alu_out WHEN "100",
          alu_out WHEN "101",
          alu_out WHEN "110",
          alu_out WHEN "111";
END BLOCK;
--
--
--
--
-- Arithmetic and Logic Unit 
--
--  r+s
--
sumrs(3 DOWNTO 0)   <=  r(3 DOWNTO 0) XOR 
                        s(3 DOWNTO 0) XOR 
                       (c_sumrs(2 DOWNTO 0) & cin);
c_sumrs(3 DOWNTO 0) <= (r(3 DOWNTO 0) AND s(3 DOWNTO 0)) OR 
                       (r(3 DOWNTO 0) AND (c_sumrs(2 DOWNTO 0) & cin)) OR
                       (s(3 DOWNTO 0) AND (c_sumrs(2 DOWNTO 0) & cin));
--
--  r-s
--
difrs(3 DOWNTO 0)   <=  r(3 DOWNTO 0) XOR 
                        NOT s(3 DOWNTO 0) XOR 
                       (c_difrs(2 DOWNTO 0) & cin);
c_difrs(3 DOWNTO 0) <= (r(3 DOWNTO 0) AND NOT s(3 DOWNTO 0)) OR 
                       (r(3 DOWNTO 0) AND (c_difrs(2 DOWNTO 0) & cin)) OR
                       (NOT s(3 DOWNTO 0) AND (c_difrs(2 DOWNTO 0) & cin));
--
--  s-r
--
difsr(3 DOWNTO 0)   <=  NOT r(3 DOWNTO 0) XOR 
                        s(3 DOWNTO 0) XOR 
                       (c_difsr(2 DOWNTO 0) & cin);
c_difsr(3 DOWNTO 0) <= (NOT r(3 DOWNTO 0) AND s(3 DOWNTO 0)) OR 
                       (NOT r(3 DOWNTO 0) AND (c_difsr(2 DOWNTO 0) & cin)) OR
                       (s(3 DOWNTO 0) AND (c_difsr(2 DOWNTO 0) & cin));
--
-- P &G FLAGS
--
WITH i(5 DOWNTO 3) SELECT
ng <= NOT ((r(3) AND s(3)) OR ((r(3) OR s(3)) AND (r(2) AND s(2))) OR ((r(3) OR s(3)) AND (r(2) OR s(2)) AND (r(1) AND s(1))) OR ((r(3) OR s(3)) AND (r(2) OR s(2)) AND (r(1) OR s(1)) AND (r(0) AND s(0)))) WHEN "000",
      NOT ((NOT r(3) AND s(3)) OR ((NOT r(3) OR s(3)) AND (NOT r(2) AND s(2))) OR ((NOT r(3) OR s(3)) AND (NOT r(2) OR s(2)) AND (NOT r(1) AND s(1))) OR ((NOT r(3) OR s(3)) AND (NOT r(2) OR s(2)) AND (NOT r(1) OR s(1)) AND (NOT r(0) AND s(0)))) WHEN "001",
      NOT ((r(3) AND NOT s(3)) OR ((r(3) OR NOT s(3)) AND (r(2) AND NOT s(2))) OR ((r(3) OR NOT s(3)) AND (r(2) OR NOT s(2)) AND (r(1) AND NOT s(1))) OR ((r(3) OR NOT s(3)) AND (r(2) OR NOT s(2)) AND (r(1) OR NOT s(1)) AND (r(0) AND NOT s(0))))WHEN "010",
      "1" WHEN OTHERS;
--
--
WITH i(5 DOWNTO 3) SELECT
np <= NOT ((r(3) OR s(3)) AND (r(2) OR s(2)) AND (r(1) OR s(1)) AND (r(0) OR s(0))) WHEN "000",
      NOT ((NOT r(3) OR s(3)) AND (NOT r(2) OR s(2)) AND (NOT r(1) OR s(1)) AND (NOT r(0) OR s(0))) WHEN "001",
      NOT ((r(3) OR NOT s(3)) AND (r(2) OR NOT s(2)) AND (r(1) OR NOT s(1)) AND (r(0) OR NOT s(0))) WHEN "010",
      "1" WHEN OTHERS;
--
--
signe <= alu_out(3);
zero <= NOT (alu_out(3) OR alu_out(2) OR alu_out(1) OR alu_out(0));
--
--
WITH i(5 DOWNTO 3) SELECT
ovr <= c_sumrs(3) XOR c_sumrs(2) WHEN "000",
       c_difsr(3) XOR c_difsr(2) WHEN "001",
       c_difrs(3) XOR c_difrs(2) WHEN "010",
       "0"                       WHEN OTHERS;
--
--
WITH i(5 DOWNTO 3) SELECT
alu_out <= sumrs         WHEN "000",
           difsr         WHEN "001",
           difrs         WHEN "010",
           r OR s        WHEN "011",
           r AND s       WHEN "100",
           NOT(r) AND s  WHEN "101",
           r XOR s       WHEN "110",
           NOT(r XOR s)  WHEN "111";
--
--
WITH i(5 DOWNTO 3) SELECT
cout <= c_sumrs(3) WHEN "000",
        c_difsr(3) WHEN "001",
        c_difrs(3) WHEN "010",
        "0"        WHEN OTHERS;
--
--
--
--
-- CONTROL SIGNALS
--
--
wram <= (i(8) OR i(7)) AND fonc_mode;
waccu <= NOT (i(6)) AND ((NOT i(7)) OR i(8));
fonc_mode <= fonc AND (NOT test);
test_mode <= test AND (NOT fonc);
shift_r <= i(8) AND (NOT i(7));  
shift_l <= i(8) AND i(7);  
shift_n <= NOT i(8);  
--
--
--
--
-- SHIFTER ACCU
--
WITH i(8 DOWNTO 6) SELECT
     sh_acc(3) <= alu_out(3)        WHEN "000",
                  NOT accu(2)       WHEN "110",
                  NOT accu(2)       WHEN "111",
                  q3                WHEN "100",
                  q3                WHEN "101",
                  "0"               WHEN OTHERS;
--
WITH i(8 DOWNTO 6) SELECT
     sh_acc(2) <= alu_out(2)        WHEN "000",
                  NOT accu(1)       WHEN "110",
                  NOT accu(1)       WHEN "111",
                  NOT accu(3)       WHEN "100",
                  NOT accu(3)       WHEN "101",
                  "0"               WHEN OTHERS;
--
WITH i(8 DOWNTO 6) SELECT
     sh_acc(1) <= alu_out(1)        WHEN "000",
                  NOT accu(0)       WHEN "110",
                  NOT accu(0)       WHEN "111",
                  NOT accu(2)       WHEN "100",
                  NOT accu(2)       WHEN "101",
                  "0"               WHEN OTHERS;
--
WITH i(8 DOWNTO 6) SELECT
     sh_acc(0) <= alu_out(0)        WHEN "000",
                  q0                WHEN "110",
                  q0                WHEN "111",
                  NOT accu(1)       WHEN "100",
                  NOT accu(1)       WHEN "101",
                  "0"               WHEN OTHERS;
--
--
--
--
esq3 : BLOCK (i(8 DOWNTO 6)="110" OR i(8 DOWNTO 6)="111")
BEGIN
     q3 <=  GUARDED NOT accu(3);
END BLOCK;
--
esq0 : BLOCK (i(8 DOWNTO 6)="100" OR i(8 DOWNTO 6)="101")
BEGIN
     q0 <=  GUARDED NOT accu(0);
END BLOCK;
--
--
--
--
-- WRITING ACCU
--
wckaccu <= ck AND (test_mode OR (waccu AND fonc_mode));
WITH test_mode SELECT
accu_in(3 DOWNTO 0) <= sh_acc(3 DOWNTO 0) WHEN "0" ,
                       (NOT accu(2 DOWNTO 0)) & scin WHEN "1" ;

accu : BLOCK ((wckaccu='1') AND NOT wckaccu'STABLE)
BEGIN
accu <= GUARDED NOT accu_in;
END BLOCK accu;
--
--
scout <= NOT accu(3) AND test_mode ;
--
--
--
--
-- SHIFTER - RAM 
--
--
WITH shift_n & shift_l & shift_r SELECT
       sh_ram(3) <= alu_out(3) WHEN "100",
                    alu_out(2) WHEN "010",
                    r3         WHEN "001",
                    "0"        WHEN OTHERS;
--
WITH shift_n & shift_l & shift_r SELECT
       sh_ram(2) <= alu_out(2) WHEN "100",
                    alu_out(1) WHEN "010",
                    alu_out(3) WHEN "001",
                    "0"        WHEN OTHERS;
--
WITH shift_n & shift_l & shift_r SELECT
       sh_ram(1) <= alu_out(1) WHEN "100",
                    alu_out(0) WHEN "010",
                    alu_out(2) WHEN "001",
                    "0"        WHEN OTHERS;
--
WITH shift_n & shift_l & shift_r SELECT
       sh_ram(0) <= alu_out(0) WHEN "100",
                    r0         WHEN "010",
                    alu_out(1) WHEN "001",
                    "0"        WHEN OTHERS;
--
--
--
--
esr3 : BLOCK (shift_l='1')
BEGIN
     r3 <=  GUARDED alu_out(3);
END BLOCK;
--
esr0 : BLOCK (shift_r='1')
BEGIN
     r0 <=  GUARDED alu_out(0);
END BLOCK;
--
--
-- Writing RAM adress b
--

-- b="0000"
wck0 <= ck AND wram AND NOT b(3) AND NOT b(2) AND NOT b(1) AND NOT b(0);
RM0 : BLOCK ((wck0='1') AND NOT wck0'STABLE)
BEGIN
ram0 <= GUARDED NOT sh_ram;
END BLOCK;

-- b="0001"
wck1 <= ck AND wram AND NOT b(3) AND NOT b(2) AND NOT b(1) AND b(0);
RM1 : BLOCK ((wck1='1') AND NOT wck1'STABLE)
BEGIN
ram1 <= GUARDED NOT sh_ram;  
END BLOCK;
 
-- b="0010"
wck2 <= ck AND wram AND NOT b(3) AND NOT b(2) AND b(1) AND NOT b(0);
RM2 : BLOCK ((wck2='1') AND NOT wck2'STABLE)
BEGIN
ram2 <= GUARDED NOT sh_ram;
END BLOCK;

-- b="0011"
wck3 <= ck AND wram AND NOT b(3) AND NOT b(2) AND b(1) AND b(0);
RM3 : BLOCK ((wck3='1') AND NOT wck3'STABLE)
BEGIN
ram3 <= GUARDED NOT sh_ram;  
END BLOCK;

-- b="0100"
wck4 <= ck AND wram AND NOT b(3) AND b(2) AND NOT b(1) AND NOT b(0);
RM4 : BLOCK ((wck4='1') AND NOT wck4'STABLE)
BEGIN
ram4 <= GUARDED NOT sh_ram;
END BLOCK;

-- b="0101"
wck5 <= ck AND wram AND NOT b(3) AND b(2) AND NOT b(1) AND b(0);
RM5 : BLOCK ((wck5='1') AND NOT wck5'STABLE)
BEGIN
ram5 <= GUARDED NOT sh_ram;  
END BLOCK;

-- b="0110"
wck6 <= ck AND wram AND NOT b(3) AND b(2) AND b(1) AND NOT b(0);
RM6 : BLOCK ((wck6='1') AND NOT wck6'STABLE)
BEGIN
ram6 <= GUARDED NOT sh_ram;
END BLOCK;

-- b="0111"
wck7 <= ck AND wram AND NOT b(3) AND b(2) AND b(1) AND b(0);
RM7 : BLOCK ((wck7='1') AND NOT wck7'STABLE)
BEGIN
ram7 <= GUARDED NOT sh_ram;  
END BLOCK;

-- b="1000"
wck8 <= ck AND wram AND b(3) AND NOT b(2) AND NOT b(1) AND NOT b(0);
RM8 : BLOCK ((wck8='1') AND NOT wck8'STABLE)
BEGIN
ram8 <= GUARDED NOT sh_ram;
END BLOCK;

-- b="1001"
wck9 <= ck AND wram AND b(3) AND NOT b(2) AND NOT b(1) AND b(0);
RM9 : BLOCK ((wck9='1') AND NOT wck9'STABLE)
BEGIN
ram9 <= GUARDED NOT sh_ram;  
END BLOCK;

-- b="1010"
wck10 <= ck AND wram AND b(3) AND NOT b(2) AND b(1) AND NOT b(0);
RM10 : BLOCK ((wck10='1') AND NOT wck10'STABLE)
BEGIN
ram10 <= GUARDED NOT sh_ram;
END BLOCK;

-- b="1011"
wck11 <= ck AND wram AND b(3) AND NOT b(2) AND b(1) AND b(0);
RM11 : BLOCK ((wck11='1') AND NOT wck11'STABLE)
BEGIN
ram11 <= GUARDED NOT sh_ram;  
END BLOCK;

-- b="1100"
wck12 <= ck AND wram AND b(3) AND b(2) AND NOT b(1) AND NOT b(0);
RM12 : BLOCK ((wck12='1') AND NOT wck12'STABLE)
BEGIN
ram12 <= GUARDED NOT sh_ram;
END BLOCK;

-- b="1101"
wck13 <= ck AND WRAM and b(3) AND b(2) AND NOT b(1) AND b(0);
RM13 : BLOCK ((wck13='1') AND NOT wck13'STABLE)
BEGIN
ram13 <= GUARDED NOT sh_ram;  
END BLOCK;

-- b="1110"
wck14 <= ck AND WRAM and b(3) AND b(2) AND b(1) AND NOT b(0);
RM14 : BLOCK ((wck14='1') AND NOT wck14'STABLE)
BEGIN
ram14 <= GUARDED NOT sh_ram;
END BLOCK;

-- b="1111"
wck15 <= ck AND WRAM and b(3) AND b(2) AND b(1) AND b(0);
RM15 : BLOCK ((wck15='1') AND NOT wck15'STABLE)
BEGIN
ram15 <= GUARDED NOT sh_ram;  
END BLOCK;
--
--
--
-- Reading RAM address b
--
with b select 
    rb <= NOT ram0  WHEN "0000",
          NOT ram1  WHEN "0001",
          NOT ram2  WHEN "0010",
          NOT ram3  WHEN "0011",
          NOT ram4  WHEN "0100",
          NOT ram5  WHEN "0101",
          NOT ram6  WHEN "0110",
          NOT ram7  WHEN "0111",
          NOT ram8  WHEN "1000",
          NOT ram9  WHEN "1001",
          NOT ram10 WHEN "1010",
          NOT ram11 WHEN "1011",
          NOT ram12 WHEN "1100",
          NOT ram13 WHEN "1101",
          NOT ram14 WHEN "1110",
          NOT ram15 WHEN "1111";
--
--
--
-- Reading RAM address a
--
with a select 
    ra <= NOT ram0  WHEN "0000",
          NOT ram1  WHEN "0001",
          NOT ram2  WHEN "0010",
          NOT ram3  WHEN "0011",
          NOT ram4  WHEN "0100",
          NOT ram5  WHEN "0101",
          NOT ram6  WHEN "0110",
          NOT ram7  WHEN "0111",
          NOT ram8  WHEN "1000",
          NOT ram9  WHEN "1001",
          NOT ram10 WHEN "1010",
          NOT ram11 WHEN "1011",
          NOT ram12 WHEN "1100",
          NOT ram13 WHEN "1101",
          NOT ram14 WHEN "1110",
          NOT ram15 WHEN "1111";
--
--
END data_flow;  
