uart_rx: ein prozessmodell. spart weitere 3 logic elements :P
[hwmod.git] / src / parser.vhd
index 6e41837ade3e3eaf1783d3a84df288b6de2845e1..4ad14cc9246a34443f2aa2ebeb710632d5294eaa 100644 (file)
@@ -4,13 +4,10 @@ use ieee.numeric_std.all;
 use work.gen_pkg.all;
 
 entity parser is
-       port
-       (
+       port (
                sys_clk : in std_logic;
                sys_res_n : in std_logic;
                -- History
-               p_rw : out std_logic;
-               p_spalte : out hspalte;
                p_rget : out std_logic;
                p_rdone : in std_logic;
                p_read : in hbyte;
@@ -18,14 +15,6 @@ entity parser is
                p_wdone : in std_logic;
                p_write : out hbyte;
                p_finished : out std_logic;
-               -- ALU
-               opcode : out alu_ops;
-               op1 : out csigned;
-               op2 : out csigned;
-               op3 : in csigned;
-               do_calc : out std_logic;
-               calc_done : in std_logic;
-               -- TODO: calc_error : in std_logic;
                -- Scanner
                do_it : in std_logic;
                finished : out std_logic
@@ -33,106 +22,528 @@ entity parser is
 end entity parser;
 
 architecture beh of parser is
-       type PARSER_STATE is (SIDLE, SREAD_CHAR1, SREAD_CHAR2, SWRITE_CHAR);
+       type PARSER_STATE is (SIDLE, SREAD_NEWNUMBER, SREAD_SPACE_GET,
+               SREAD_SPACE_GET_SIGN, SREAD_SPACE_PROC, SREAD_SPACE_PROC_SIGN, SREAD_OP1, SREAD_OP2,
+               SREAD_SIGN, SREAD_NEXTBYTE, SREAD_CALCNUMBER1, SREAD_CALCNUMBER2, SCALC_1,
+               SCALC_14, SCALC_15, SCALC_2, SWRITE_CHAR0, SWRITE_CHAR1, SWRITE_CHAR2,
+               SWRITE_SIGN1, SWRITE_SIGN2, SDONE, SERROR1, SERROR2, SBLANK1, SBLANK2);
        signal state_int, state_next : PARSER_STATE;
-       signal z_int, z_next : csigned;
+       signal z_int, z_next, strich_int, strich_next, wtmp_int, wtmp_next : csigned;
+       signal punkt_int, punkt_next : csigned;
        signal rbyte_int, rbyte_next : hbyte;
        signal p_write_int, p_write_next : hbyte;
        signal p_rget_int, p_rget_next : std_logic;
        signal p_wtake_int, p_wtake_next : std_logic;
        signal p_finished_int, p_finished_next : std_logic;
+       signal finished_int, finished_next : std_logic;
+       signal aktop_int, aktop_next : alu_ops;
+       signal opp_int, opp_next : alu_ops;
+       signal opcode_int, opcode_next : alu_ops;
+       signal op1_int, op1_next : csigned;
+       signal op2_int, op2_next : csigned;
+       signal do_calc_int, do_calc_next : std_logic;
+       signal z_sign_next, z_sign_int : std_logic;
+       signal firstz_next, firstz_int : boolean;
+       signal err_next, err_int : hstr_int;
+       signal errc_next, errc_int : hstr_int;
+       -- ALU
+       signal opcode : alu_ops;
+       signal op1 : csigned;
+       signal op2 : csigned;
+       signal op3 : csigned;
+       signal opM : csigned;
+       signal do_calc : std_logic;
+       signal calc_done : std_logic;
+       signal calc_error : std_logic;
 begin
+       instalu : alu
+       port map (
+               sys_clk => sys_clk,
+               sys_res_n => sys_res_n,
+               do_calc => do_calc,
+               calc_done => calc_done,
+               calc_error => calc_error,
+               op1 => op1,
+               op2 => op2,
+               op3 => op3,
+               opM => opM,
+               opcode => opcode
+       );
+
        p_write <= p_write_int;
        p_rget <= p_rget_int;
        p_wtake <= p_wtake_int;
        p_finished <= p_finished_int;
+       finished <= finished_int;
+
+       opcode <= opcode_int;
+       op1 <= op1_int;
+       op2 <= op2_int;
+       do_calc <= do_calc_int;
 
        process(sys_clk, sys_res_n)
        begin
                if sys_res_n = '0' then
                        state_int <= SIDLE;
                        z_int <= (others => '0');
+                       z_sign_int <= '0';
+                       strich_int <= (others => '0');
+                       punkt_int <= (others => '0');
+                       wtmp_int <= (others => '0');
                        rbyte_int <= (others => '0');
+                       aktop_int <= ALU_NOP;
+                       opp_int <= ALU_NOP;
+                       err_int <= 0;
+                       errc_int <= HSPALTE_MAX;
+                       firstz_int <= true;
                        -- out ports
-                       p_rw <= '0';
-                       p_spalte <= (others => '0');
                        p_rget_int <= '0';
                        p_write_int <= (others => '0');
                        p_wtake_int <= '0';
                        p_finished_int <= '0';
-                       opcode <= ALU_NOP;
-                       op1 <= (others => '0');
-                       op2 <= (others => '0');
-                       do_calc <= '0';
-                       finished <= '0';
+                       finished_int <= '0';
+                       opcode_int <= ALU_NOP;
+                       op1_int <= (others => '0');
+                       op2_int <= (others => '0');
+                       do_calc_int <= '0';
                elsif rising_edge(sys_clk) then
                        -- internal
                        state_int <= state_next;
                        z_int <= z_next;
+                       z_sign_int <= z_sign_next;
+                       strich_int <= strich_next;
+                       punkt_int <= punkt_next;
+                       wtmp_int <= wtmp_next;
                        rbyte_int <= rbyte_next;
+                       aktop_int <= aktop_next;
+                       opp_int <= opp_next;
+                       err_int <= err_next;
+                       errc_int <= errc_next;
+                       firstz_int <= firstz_next;
                        -- out ports
                        p_rget_int <= p_rget_next;
                        p_write_int <= p_write_next;
                        p_wtake_int <= p_wtake_next;
                        p_finished_int <= p_finished_next;
+                       finished_int <= finished_next;
+                       opcode_int <= opcode_next;
+                       op1_int <= op1_next;
+                       op2_int <= op2_next;
+                       do_calc_int <= do_calc_next;
                end if;
        end process;
 
        -- next state
-       process(state_int, do_it, p_rdone, p_wdone, p_read)
-       begin
-               state_next <= state_int;
+       process(do_it, p_rdone, p_wdone, p_read, aktop_int, strich_int, punkt_int,
+                       calc_done, wtmp_int, opp_int, z_sign_int, err_int, errc_int,
+                       calc_error, op2_int, state_int, p_write_int, z_int, rbyte_int,
+                       p_rget_int, opcode_int, op1_int, op3, opM, do_calc_int,
+                       firstz_int)
+               function hbyte2csigned (x : hbyte) return csigned is
+                       variable y : csigned;
+               begin
+                       case x is
+                               when x"30" => y := x"00000000";
+                               when x"31" => y := x"00000001";
+                               when x"32" => y := x"00000002";
+                               when x"33" => y := x"00000003";
+                               when x"34" => y := x"00000004";
+                               when x"35" => y := x"00000005";
+                               when x"36" => y := x"00000006";
+                               when x"37" => y := x"00000007";
+                               when x"38" => y := x"00000008";
+                               when x"39" => y := x"00000009";
+                               when others => assert(false) report "hbyte2csigned: shouldn't happen";
+                       end case;
+                       return y;
+               end function hbyte2csigned;
 
-               case state_int is
-                       when SIDLE =>
-                               if do_it = '1' then
-                                       state_next <= SREAD_CHAR1;
-                               end if;
-                       when SREAD_CHAR1 =>
-                               if p_rdone = '1' then
-                                       state_next <= SREAD_CHAR2;
-                               end if;
-                       when SREAD_CHAR2 =>
-                               if p_wdone = '1' then
-                                       state_next <= SWRITE_CHAR;
-                               end if;
-                       when SWRITE_CHAR =>
-                               if rbyte_int = hbyte(to_unsigned(character'pos(character'val(0)), 8)) then
-                                       if do_it = '0' then
-                                               state_next <= SIDLE;
-                                       end if;
-                               else
-                                       state_next <= SREAD_CHAR1;
-                               end if;
-               end case;
-       end process;
+               function csigned2hbyte (x : csigned) return hbyte is
+                       variable y : hbyte;
+               begin
+                       case x is
+                               when x"00000000" => y := x"30";
+                               when x"00000001" => y := x"31";
+                               when x"00000002" => y := x"32";
+                               when x"00000003" => y := x"33";
+                               when x"00000004" => y := x"34";
+                               when x"00000005" => y := x"35";
+                               when x"00000006" => y := x"36";
+                               when x"00000007" => y := x"37";
+                               when x"00000008" => y := x"38";
+                               when x"00000009" => y := x"39";
+                               when others => assert(false) report "csigned2hbyte: shouldn't happen";
+                       end case;
+                       return y;
+               end function csigned2hbyte;
+
+               variable multmp : signed(((2*CBITS)-1) downto 0);
+               variable tmp : csigned;
 
-       process(state_int, p_read, p_write_int, z_int, rbyte_int, p_rget_int)
+               type errstrings is array (natural range 1 to 3) of hstring;
+               constant error_str : errstrings := (
+                       1 => " Fehler:                             Division durch Null               " & nul,
+                       2 => " Fehler:                             Syntax                            " & nul,
+                       3 => " Fehler:                             Over- bzw. Underflow              " & nul
+               );
        begin
+               state_next <= state_int;
                -- internal
                z_next <= z_int;
+               z_sign_next <= z_sign_int;
+               strich_next <= strich_int;
+               punkt_next <= punkt_int;
+               wtmp_next <= wtmp_int;
                rbyte_next <= rbyte_int;
+               aktop_next <= aktop_int;
+               opp_next <= opp_int;
+               err_next <= err_int;
+               errc_next <= errc_int;
+               firstz_next <= firstz_int;
                -- signals
                p_rget_next <= '0';
                p_write_next <= p_write_int;
                p_wtake_next <= '0';
                p_finished_next <= '0';
+               finished_next <= '0';
+               opcode_next <= opcode_int;
+               op1_next <= op1_int;
+               op2_next <= op2_int;
+               do_calc_next <= '0';
 
                case state_int is
                        when SIDLE =>
+                               strich_next <= (others => '0');
+                               punkt_next <= (0 => '1', others => '0');
+                               opp_next <= ALU_NOP;
+                               if do_it = '1' then
+                                       state_next <= SREAD_NEWNUMBER;
+                               end if;
+
+                       when SREAD_NEWNUMBER =>
                                z_next <= (others => '0');
+                               z_sign_next <= '0';
+                               firstz_next <= true;
                                rbyte_next <= (others => '0');
                                p_write_next <= (others => '0');
-                       when SREAD_CHAR1 =>
+                               aktop_next <= ALU_NOP;
+                               state_next <= SREAD_SPACE_GET;
+
+                       when SREAD_SPACE_GET =>
                                p_rget_next <= '1';
-                               p_write_next <= (others => '0');
-                       when SREAD_CHAR2 =>
-                               rbyte_next <= p_read;
+                               if p_rdone = '1' then
+                                       state_next <= SREAD_SPACE_PROC;
+                               end if;
+                       when SREAD_SPACE_GET_SIGN =>
+                               p_rget_next <= '1';
+                               if p_rdone = '1' then
+                                       state_next <= SREAD_SPACE_PROC_SIGN;
+                               end if;
+                       when SREAD_SPACE_PROC | SREAD_SPACE_PROC_SIGN =>
+                               if p_rdone = '0' then
+                                       case state_int is
+                                               when SREAD_SPACE_PROC => state_next <= SREAD_SPACE_GET;
+                                               when SREAD_SPACE_PROC_SIGN => state_next <= SREAD_SPACE_GET_SIGN;
+                                               when others => assert(false) report "wtf @ state1";
+                                       end case;
+                               else
+                                       if p_read = x"2d" and state_int = SREAD_SPACE_PROC then
+                                               -- vorzeichen?
+                                               state_next <= SREAD_SIGN;
+                                       elsif p_read /= x"20" then
+                                               -- leerzeichen sollen ignoriert werden
+                                               p_rget_next <= '1';
+                                               case state_int is
+                                                       when SREAD_SPACE_PROC => state_next <= SREAD_NEXTBYTE;
+                                                       when SREAD_SPACE_PROC_SIGN => state_next <= SREAD_OP1;
+                                                       when others => assert(false) report "SREAD_SPACE_PROC{,_SIGN}: shouldn't happen";
+                                               end case;
+                                       end if;
+                               end if;
+
+                       when SREAD_SIGN =>
+                               z_sign_next <= '1';
+                               if p_rdone = '0' then
+                                       state_next <= SREAD_NEXTBYTE;
+                               end if;
+                       when SREAD_NEXTBYTE =>
+                               p_rget_next <= '1';
+                               if p_rdone = '1' then
+                                       state_next <= SREAD_CALCNUMBER1;
+                               end if;
+                       when SREAD_CALCNUMBER1 =>
+                               case p_read is
+                                       -- '+', '-', '*', '/'
+                                       when x"2B" | x"2D" | x"2A" | x"2F" | x"00" =>
+                                               if firstz_int then
+                                                       err_next <= 2;
+                                               else
+                                                       state_next <= SREAD_OP1;
+                                                       p_rget_next <= '1';
+                                               end if;
+
+                                       -- ' '
+                                       when x"20" =>
+                                               state_next <= SREAD_SPACE_PROC_SIGN;
+                                               p_rget_next <= '1';
+
+                                       when others =>
+                                               op1_next <= z_int;
+                                               opcode_next <= ALU_MUL;
+                                               op2_next <= to_signed(10,CBITS);
+                                               firstz_next <= false;
+                                               do_calc_next <= '1';
+                               end case;
+                               if calc_done = '1' then
+                                       state_next <= SREAD_CALCNUMBER2;
+                               end if;
+                       when SREAD_CALCNUMBER2 =>
+                               z_next <= op3 + hbyte2csigned(p_read);
+                               if p_rdone = '0' and calc_done = '0' then
+                                       state_next <= SREAD_NEXTBYTE;
+                               end if;
+
+                       when SREAD_OP1 =>
+                               case p_read is
+                                       when x"2B" => aktop_next <= ALU_ADD; -- '+'
+                                       when x"2D" => aktop_next <= ALU_SUB; -- '-'
+                                       when x"2A" => aktop_next <= ALU_MUL; -- '*'
+                                       when x"2F" => aktop_next <= ALU_DIV; -- '/'
+                                       when x"00" => aktop_next <= ALU_DONE; -- '\0'
+
+                                       when others => err_next <= 2;
+                               end case;
+                               state_next <= SREAD_OP2;
+                       when SREAD_OP2 =>
+                               if p_rdone = '0' then
+                                       state_next <= SCALC_1;
+                               end if;
+
+                       when SCALC_1 =>
+                               if z_sign_int = '1' then
+                                       tmp := (not z_int) + 1;
+                                       z_next <= tmp;
+                                       z_sign_next <= '0';
+                               else
+                                       tmp := z_int;
+                               end if;
+
+                               case opp_int is
+                                       when ALU_NOP | ALU_ADD | ALU_SUB =>
+                                               case opp_int  is
+                                                       when ALU_SUB =>
+                                                               -- xst (xilinx) workaround
+                                                               if x"80000000" = tmp then
+                                                                       -- vgl. testfall 37 und 38
+                                                                       err_next <= 3;
+                                                                       op1_next <= tmp;
+                                                               else
+                                                                       op1_next <= (not tmp) + 1;
+                                                               end if;
+                                                       when others => op1_next <= tmp;
+                                               end case;
+                                               case aktop_int is
+                                                       when ALU_ADD | ALU_SUB | ALU_DONE =>
+                                                               opcode_next <= ALU_ADD;
+                                                               op2_next <= strich_int;
+                                                       when ALU_MUL | ALU_DIV =>
+                                                               opcode_next <= ALU_MUL;
+                                                               op2_next <= punkt_int;
+                                                       when others => assert(false) report "SCALC_1/1: shouldn't happen!";
+                                               end case;
+
+                                       when ALU_MUL | ALU_DIV =>
+                                               case aktop_int is
+                                                       when ALU_ADD | ALU_SUB | ALU_DONE | ALU_MUL | ALU_DIV =>
+                                                               op1_next <= punkt_int;
+                                                               opcode_next <= opp_int;
+                                                               op2_next <= tmp;
+                                                       when others => assert(false) report "SCALC_1/2: shouldn't happen!";
+                                               end case;
+                                       when others => assert(false) report "SCALC_1/3: shouldn't happen!";
+                               end case;
+                               do_calc_next <= '1';
+
+                               if calc_done = '1' then
+                                       case opp_int is
+                                               -- spezialfall: eine zwischenberechnung wird fuer diese
+                                               -- kombination benoetigt
+                                               when ALU_MUL | ALU_DIV =>
+                                                       case aktop_int is
+                                                               when ALU_ADD | ALU_SUB | ALU_DONE => state_next <= SCALC_14;
+                                                               when others => state_next <= SCALC_2;
+                                                       end case;
+                                               when others => state_next <= SCALC_2;
+                                       end case;
+                               end if;
+                       when SCALC_14 =>
+                               -- ueberpruefung kann man sich sparen, da diese ohnehin in
+                               -- nextstate gemacht wird.
+                               op1_next <= op3;
+                               do_calc_next <= '0';
+
+                               if calc_done = '0' then
+                                       state_next <= SCALC_15;
+                               end if;
+                       when SCALC_15 =>
+                               -- ueberpruefung kann man sich sparen, da diese ohnehin in
+                               -- nextstate gemacht wird.
+                               opcode_next <= ALU_ADD;
+                               op2_next <= strich_int;
+                               punkt_next <= (0 => '1', others => '0');
+                               do_calc_next <= '1';
+
+                               if calc_done = '1' then
+                                       state_next <= SCALC_2;
+                               end if;
+                       when SCALC_2 =>
+                               case opp_int is
+                                       when ALU_NOP | ALU_ADD | ALU_SUB | ALU_MUL | ALU_DIV =>
+                                               case aktop_int is
+                                                       when ALU_ADD | ALU_SUB | ALU_DONE =>
+                                                               if aktop_int = ALU_DONE and op3 < 0 then
+                                                                       strich_next <= (not op3) + 1;
+                                                                       wtmp_next <= (not op3) + 1;
+                                                                       z_sign_next <= '1';
+                                                               else
+                                                                       strich_next <= op3;
+                                                                       wtmp_next <= op3;
+                                                               end if;
+                                                       when ALU_MUL | ALU_DIV =>
+                                                               punkt_next <= op3;
+                                                       when others => assert (false) report "SCALC_2/1: shouldn't happen!";
+                                               end case;
+                                       when ALU_DONE => null;
+                                       when others => assert (false) report "SCALC_2/2: shouldn't happen!";
+                               end case;
+                               -- aktuelle rechenoperation fuer naechste 'runde' uebernehmen
+                               opp_next <= aktop_int;
+
+                               if calc_done = '0' then
+                                       if aktop_int = ALU_DONE then
+                                               state_next <= SWRITE_CHAR2;
+                                       else
+                                               state_next <= SREAD_NEWNUMBER;
+                                       end if;
+                               end if;
+
+                       when SWRITE_CHAR0 =>
+                               -- fuer testfall 39 und 40
+                               if strich_int = to_signed(-214748364,CBITS) then
+                                       op1_next <= to_signed(214748364,CBITS);
+                                       strich_next <= to_signed(214748364,CBITS);
+                               else
+                                       op1_next <= strich_int;
+                               end if;
+                               opcode_next <= ALU_DIV;
+                               op2_next <= to_signed(10,CBITS);
+                               do_calc_next <= '1';
+
+                               if calc_done = '1' then
+                                       state_next <= SWRITE_CHAR1;
+                               end if;
+                       when SWRITE_CHAR1 =>
+                               do_calc_next <= '1';
+                               p_wtake_next <= '1';
+                               tmp := opM;
+                               p_write_next <= csigned2hbyte(tmp);
+                               wtmp_next <= op3;
+
+                               if p_wdone = '1' then
+                                       errc_next <= errc_int - 1;
+                                       -- ueberpruefung auf -2147483648 fuer testfall 39 und 40
+                                       -- x"80000000": xst (xilinx) workaround
+                                       if strich_int < 10 and strich_int /= x"80000000" then
+                                               if z_sign_int = '1' then
+                                                       state_next <= SWRITE_SIGN1;
+                                               else
+                                                       state_next <= SBLANK1;
+                                               end if;
+                                       else
+                                               state_next <= SWRITE_CHAR2;
+                                       end if;
+                               end if;
+                       when SWRITE_CHAR2 =>
+                               strich_next <= wtmp_int;
+                               if p_wdone = '0' and calc_done = '0' then
+                                       state_next <= SWRITE_CHAR0;
+                               end if;
+
+                       when SWRITE_SIGN1 =>
+                               if p_wdone = '0' then
+                                       state_next <= SWRITE_SIGN2;
+                               end if;
+                       when SWRITE_SIGN2 =>
+                               if z_sign_int = '1' then
+                                       p_wtake_next <= '1';
+                                       p_write_next <= x"2D";
+                               else
+                                       assert(false) report "SWRITE_SIGN: shouldn't happen!";
+                               end if;
+
+                               if p_wdone = '1' then
+                                       errc_next <= errc_int - 1;
+                                       state_next <= SDONE;
+                               end if;
+
+                       when SBLANK1 =>
+                               p_wtake_next <= '1';
+                               p_write_next <= x"20";
+                               if p_wdone = '1' then
+                                       errc_next <= errc_int - 1;
+                                       if errc_int <= 2 then
+                                               state_next <= SDONE;
+                                       else
+                                               state_next <= SBLANK2;
+                                       end if;
+                               end if;
+                       when SBLANK2 =>
+                               if p_wdone = '0' then
+                                       state_next <= SBLANK1;
+                               end if;
+
+                       when SERROR1 =>
                                p_wtake_next <= '1';
-                               p_write_next <= p_read;
-                       when SWRITE_CHAR =>
-                               if rbyte_int = hbyte(to_unsigned(character'pos(character'val(0)), 8)) then
-                                       p_finished_next <= '1';
+                               p_write_next <= hbyte(to_unsigned (character'pos(error_str(err_int)(errc_int)),8));
+                               if p_wdone = '1' then
+                                       errc_next <= errc_int - 1;
+                                       if errc_int <= 2 then
+                                               state_next <= SDONE;
+                                       else
+                                               state_next <= SERROR2;
+                                       end if;
+                               end if;
+                       when SERROR2 =>
+                               if p_wdone = '0' then
+                                       state_next <= SERROR1;
+                               end if;
+
+                       when SDONE =>
+                               err_next <= 0;
+                               errc_next <= HSPALTE_MAX;
+                               p_finished_next <= '1';
+                               finished_next <= '1';
+
+                               if p_wdone = '0' and do_it = '0' then
+                                       state_next <= SIDLE;
+                               end if;
+               end case;
+
+               -- fehlerbehandlung
+               case state_int is
+                       -- diese states sind ausgenommen vom "pokemon-exception-handling"
+                       when SERROR1 | SERROR2 | SDONE => null;
+                       when others =>
+                               if calc_error = '1' then
+                                       if op2_int = 0 then
+                                               err_next <= 1;
+                                       else
+                                               err_next <= 3;
+                                       end if;
+                               end if;
+
+                               if err_int > 0 then
+                                       state_next <= SERROR1;
                                end if;
                end case;
        end process;