---------------------------------------------------------------------------------- -- Company: -- Engineer: -- -- Create Date: 16:04:58 09/23/2009 -- Design Name: -- Module Name: UART_InstrRead - beh -- Project Name: -- Target Devices: -- Tool versions: -- Description: -- -- Dependencies: -- -- Revision: -- Revision 0.01 - File Created -- Additional Comments: -- ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.all; -- clk and reset needed for UART state machine. tx_wire is output wire to UART connector. -- rx_wire is input wire from UART connector. Enter pushbutton is used to send result to -- screen, other pushbuttons to be connected to microcontroller instructions. LEDs are -- just for fun -- use them any way you want entity UARTInstrRead is Port ( clk : in std_logic; reset : in std_logic; enter_pb_not : in std_logic; up_pb_not: in std_logic; down_pb_not: in std_logic; left_pb_not: in std_logic; right_pb_not: in std_logic; LED_0_not : out std_logic; LED_1_not : out std_logic; LED_2_not : out std_logic; LED_3_not : out std_logic; rx_wire : in std_logic; tx_wire : out std_logic); end UARTInstrRead; architecture beh of UARTInstrRead is -- State machine declarations type state_type is (idle, get_char, clear_chars, dump_IR_mem, wait1, convert_BTA, send, done); signal state_reg, state_next: state_type; -- UART signals signal tx_full, rx_empty : std_logic; signal ascii_receive_data: std_logic_vector(7 downto 0); signal ascii_send_data: std_logic_vector(7 downto 0); signal send_one_byte : std_logic; signal read_byte : std_logic; -- Pushbutton and DEBOUNCE signals signal enter_pb, enter_pb_stable, enter_button_level : std_logic; signal up_pb, up_pb_stable, up_button_level : std_logic; signal down_pb, down_pb_stable, down_button_level : std_logic; signal left_pb, left_pb_stable, left_button_level : std_logic; signal right_pb, right_pb_stable, right_button_level : std_logic; -- BinToASCII signals signal BTA_ready, convert_BTA_done_tick, start_BTA_convert : std_logic; signal BTA_OUT, DEST_OUT: std_logic_vector(11 downto 0); signal R0, R1, R2, R3 : std_logic_vector(7 downto 0); signal BTA_sign_char: std_logic_vector(7 downto 0); -- ASCIIToBin signals signal A0_reg, A0_next, A1_reg, A1_next, A2_reg, A2_next, A3_reg, A3_next : std_logic_vector(7 downto 0); signal ATB_sign_char_reg, ATB_sign_char_next: std_logic_vector(7 downto 0); signal ATB_ready, convert_ATB_done_tick, start_ATB_convert : std_logic; signal ATB_out_operand: std_logic_vector(11 downto 0); -- State machine signals signal A_op_reg, A_op_next, B_op_reg, B_op_next: std_logic_vector(11 downto 0); signal cnt_reg, cnt_next : unsigned(2 downto 0); -- ALU signals signal add_ins, sub_ins, inv_ins, and_ins, or_ins, xor_ins, shltf_ins, shrtf_ins, skwlf_ins, skwgf_ins : std_logic; signal aluwf_ins, call_ins, goto_ins, retn_ins, noop_ins, skwcf_ins, movwf_ins, movfw_ins, read_ctrl, write_ctrl : std_logic; signal skwnf_ins, skwef_ins, movf_ins, movlw_ins : std_logic; signal skw_success : std_logic; -- Instruction register signals signal IR_reg: std_logic_vector(11 downto 0); signal d_bit : std_logic; -- Instruction memory signals signal scan_in, scan_en, load_sig, scan_out, go_scan_emulation : std_logic; signal PC, scan_PC_reg: std_logic_vector(7 downto 0); signal dump_PC_reg, dump_PC_next: unsigned(7 downto 0); -- DEBUG signal scan_reg: std_logic_vector(11 downto 0); -- Scan Emulation signals signal ScanIRIn_reg, ScanIRIn_next : std_logic_vector(11 downto 0); signal go_dump_IR_mem : std_logic; begin -- Invert button value for proper operation of debounce circuit enter_pb <= not enter_pb_not; up_pb <= not up_pb_not; down_pb <= not down_pb_not; left_pb <= not left_pb_not; right_pb <= not right_pb_not; -- 38400 with a 100MHz clock requires 100,000,000/(16*38400) = 163 # TICKS for stop bits (16, 24, 32 for 1, 1.5 and 2) -- Number of addr bits for FIFO sets it's size as FIFO = 2^FIFO_NUM_ADDR_BITS UARTComponent: entity work.UARTUnit(beh) generic map (BAUDGEN_CLOCK_TO_BAUD_MOD=>163, BAUDGEN_REG_SIZE=>10, DATA_BITS=>8, STOP_BITS_TICKS=>16, FIFO_NUM_ADDR_BITS=>2) port map ( clk=>clk, reset=>reset, rd_uart=>read_byte, wr_uart=>send_one_byte, rx_wire=>rx_wire, tx_loc_sys_data=>ascii_send_data, tx_full=>tx_full, rx_empty=>rx_empty, rx_loc_sys_data=>ascii_receive_data, tx_wire=>tx_wire); -- Take the bounce out of the pushbutton (only works for buttons normally at 0 and then go to 1 when pressed!) DebounceInst1: entity work.DEBOUNCE(beh) port map (clk=>clk, reset=>reset, button=>enter_pb, button_level=>enter_button_level, button_stable=>enter_pb_stable); DebounceInst2: entity work.DEBOUNCE(beh) port map (clk=>clk, reset=>reset, button=>up_pb, button_level=>up_button_level, button_stable=>up_pb_stable); DebounceInst3: entity work.DEBOUNCE(beh) port map (clk=>clk, reset=>reset, button=>down_pb, button_level=>down_button_level, button_stable=>down_pb_stable); DebounceInst4: entity work.DEBOUNCE(beh) port map (clk=>clk, reset=>reset, button=>left_pb, button_level=>left_button_level, button_stable=>left_pb_stable); DebounceInst5: entity work.DEBOUNCE(beh) port map (clk=>clk, reset=>reset, button=>right_pb, button_level=>right_button_level, button_stable=>right_pb_stable); -- BinToASCII is called to convert a binary number to ASCII with start_BTA_convert. Since numbers -- are 12 bits, this restricts there size to be between -2048 and +2047 -- the sign plus 4 digits. -- NOTE: I used this to debug my module by replacing BTA_OUT with other quantities I wanted to -- monitor!!!!! BTAInst: entity work.BinToASCII(beh) port map (clk=>clk, reset=>reset, ready=>BTA_ready, convert_done_tick=>convert_BTA_done_tick, start_convert=>start_BTA_convert, result_binary_val=>BTA_OUT, dig0=>R0, dig1=>R1, dig2=>R2, dig3=>R3, sign_char=>BTA_sign_char); -- ASCIIToBin converts an ASCII string (terminated with a carriage return) into a binary value. ATBInst: entity work.ASCIIToBin(beh) port map (clk=>clk, reset=>reset, ready=>ATB_ready, convert_done_tick=>convert_ATB_done_tick, start_convert=>start_ATB_convert, op_binary_val=>ATB_out_operand, dig0=>A0_reg, dig1=>A1_reg, dig2=>A2_reg, dig3=>A3_reg, sign_char=>ATB_sign_char_reg); -- LED is on when '0' is written. Assign these to whatever signals you want to observe LED_0_not <= not go_dump_IR_mem; LED_1_not <= '0' when scan_PC_reg = "00000000" else '1'; LED_2_not <= not reset; LED_3_not <= not skw_success; -- Instantiate the instruction decoder unit -- This get eliminated by synthesis because the instructions movwf_ins and movfw_ins are not used yet! d_bit <= IR_reg(5); InstrDecoderUnit: entity work.InstrDecoder(beh) port map (opcode=>IR_reg(11 downto 9), subcode=>IR_reg(8 downto 6), d_bit=>d_bit, prog_mode=>up_button_level, aluwf_ins=>aluwf_ins, movf_ins=>movf_ins, call_ins=>call_ins, movlw_ins=>movlw_ins, goto_ins=>goto_ins, retn_ins=>retn_ins, noop_ins=>noop_ins, skwcf_ins=>skwcf_ins, add_ins=>add_ins, sub_ins=>sub_ins, and_ins=>and_ins, or_ins=>or_ins, xor_ins=>xor_ins, inv_ins=>inv_ins, shltf_ins=>shltf_ins, shrtf_ins=>shrtf_ins, skwgf_ins=>skwgf_ins, skwlf_ins=>skwlf_ins, skwef_ins=>skwef_ins, skwnf_ins=>skwnf_ins, movwf_ins=>movwf_ins, movfw_ins=>movfw_ins, read_ctrl=>read_ctrl, write_ctrl=>write_ctrl); -- Instantiate the ALU component ALUUnit: entity work.ALU(beh) port map (A=>A_op_reg, B=>B_op_reg, DEST_OUT=>DEST_OUT, skw_success=>skw_success, inv_ins=>inv_ins, and_ins=>and_ins, or_ins=>or_ins, xor_ins=>xor_ins, add_ins=>add_ins, sub_ins=>sub_ins, shltf_ins=>shltf_ins, shrtf_ins=>shrtf_ins, skwlf_ins=>skwlf_ins, skwgf_ins=>skwgf_ins, skwnf_ins=>skwnf_ins, skwef_ins=>skwef_ins, movf_ins=>movf_ins, movlw_ins=>movlw_ins); -- =================================================================================================================== -- ADD YOUR InstrMemUnit and ScanEmulation components HERE -- see lab assignment -- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -- NOTE: Use PC (NOT scan_PC_reg) as the actual port connection to PC in the InstrMemUnit, -- i.e., PC=>PC -- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -- =================================================================================================================== PC <= std_logic_vector(dump_PC_reg) when go_dump_IR_mem = '1' else scan_PC_reg; BTA_OUT <= IR_reg; -- ===================================================================================================== -- ===================================================================================================== -- state and register logic process(clk, reset) begin if (reset = '1') then state_reg <= idle; -- This counter used when outputting the result -- counts from 0 to 6, which includes sign -- character, 4 digits of number and carriage return and line feed cnt_reg <= to_unsigned(0, 3); -- The sign register ATB_sign_char_reg <= (others => '0'); -- The actual character registers A0_reg <= (others => '0'); A1_reg <= (others => '0'); A2_reg <= (others => '0'); A3_reg <= (others => '0'); -- The converted binary operands A_op_reg <= (others => '0'); B_op_reg <= (others => '0'); ScanIRIn_reg <= (others => '0'); -- local PC used when dumping instruction memory dump_PC_reg <= (others => '0'); -- Update registers with next state value elsif (clk'event and clk = '1') then state_reg <= state_next; cnt_reg <= cnt_next; ATB_sign_char_reg <= ATB_sign_char_next; A0_reg <= A0_next; A1_reg <= A1_next; A2_reg <= A2_next; A3_reg <= A3_next; A_op_reg <= A_op_next; B_op_reg <= B_op_next; ScanIRIn_reg <= ScanIRIn_next; dump_PC_reg <= dump_PC_next; end if; end process; -- ===================================================================================================== -- Combinational Block -- little more involved than I would have liked it but manageable -- ===================================================================================================== process(state_reg, cnt_reg, R0, R1, R2, R3, BTA_ready, convert_BTA_done_tick, enter_pb_stable, BTA_sign_char, rx_empty, ascii_receive_data, A0_reg, A1_reg, A2_reg, A3_reg, tx_full, ATB_sign_char_reg, A_op_reg, B_op_reg, convert_ATB_done_tick, ATB_out_operand, ScanIRIn_reg, dump_PC_reg, scan_PC_reg) begin -- Default assignments to wires and registers (latter just preserves current value) state_next <= state_reg; cnt_next <= cnt_reg; A_op_next <= A_op_reg; B_op_next <= B_op_reg; ScanIRIn_next <= ScanIRIn_reg; ascii_send_data <= R0; send_one_byte <= '0'; read_byte <= '0'; ATB_sign_char_next <= ATB_sign_char_reg; A0_next <= A0_reg; A1_next <= A1_reg; A2_next <= A2_reg; A3_next <= A3_reg; start_ATB_convert <= '0'; start_BTA_convert <= '0'; -- Used to start the scan emulation engine. PC register defined in the scan emulation engine by default -- drives the PC address of the instruction memory. When we dump memory, we override this PC with a -- local register. go_scan_emulation <= '0'; go_dump_IR_mem <= '0'; dump_PC_next <= dump_PC_reg; case state_reg is -- --------------------------------------- -- ASCII INPUT AND OUTPUT when idle => -- Pushbutton is pressed -- start process of reading instruction memory and writing to UART if (enter_pb_stable = '1') then state_next <= dump_IR_mem; -- Initialize the PC to 0 and begin dumping the contents of the instruction memory dump_PC_next <= (others => '0'); end if; -- If the receive buffer has a character, process it if (rx_empty = '0') then -- Skip all characters that are not valid -- only valid chars are carriage return, '-' and '+', -- and numbers '0' through '9'. If character is left over linefeed -- eliminate it -- under -- Linux, this doesn't happen if ( unsigned(ascii_receive_data) = to_unsigned(13, 8) or unsigned(ascii_receive_data) = to_unsigned(character'pos('-'), 8) or unsigned(ascii_receive_data) = to_unsigned(character'pos('+'), 8) or (unsigned(ascii_receive_data) >= to_unsigned(character'pos('0'), 8) and unsigned(ascii_receive_data) <= to_unsigned(character'pos('9'), 8))) then state_next <= get_char; else read_byte <= '1'; end if; end if; -- Monitor for the convert_ATB_done_tick which indicates that a set of characters have -- been converted to a binary value. Save the ASCIIToBin output value (ATB_out_operand) -- into ScanIRIn and start the scan emulation state machine if ( convert_ATB_done_tick = '1' ) then ScanIRIn_next <= ATB_out_operand; go_scan_emulation <= '1'; end if; -- --------------------------------------- -- ASCII INPUT: If the user presses a number key, get character and save in one of four A registers -- Also, process '+', '-' and carriage return (13) when get_char => -- Check if the received value is a carriage return -- linefeed are filtered above in idle if ( unsigned(ascii_receive_data) = to_unsigned(13, 8) ) then -- Start ascii to binary conversion -- I should check ATB_ready before I do this but -- I assume there will be plenty of cycles between carriage returns start_ATB_convert <= '1'; -- Last thing to do is clear registers for next operand state_next <= clear_chars; -- If the character received in a '+' or '-', save sign. elsif ( unsigned(ascii_receive_data) = to_unsigned(character'pos('-'), 8) or unsigned(ascii_receive_data) = to_unsigned(character'pos('+'), 8)) then ATB_sign_char_next <= ascii_receive_data; state_next <= idle; -- Else assume it is a number character (0 to 9) -- create a shift reg -- this allows the user to -- enter fewer than 4 number characters register else A3_next <= A2_reg; A2_next <= A1_reg; A1_next <= A0_reg; A0_next <= ascii_receive_data; state_next <= idle; end if; -- Remove the byte from the read buffer read_byte <= '1'; -- --------------------------------------- -- ASCII INPUT: Reset pointer and character registers after the ATB routine has latched and processed -- This gets called one clock cycle AFTER carriage return is received when clear_chars => A0_next <= (others => '0'); A1_next <= (others => '0'); A2_next <= (others => '0'); A3_next <= (others => '0'); ATB_sign_char_next <= std_logic_vector(to_unsigned(character'pos('+'), 8)); state_next <= idle; -- --------------------------------------- -- --------------------------------------- -- ASCII OUTPUT: Dump IR memory when dump_IR_mem => -- clear cnter for conversion from binary to ASCII cnt_next <= (others => '0'); -- Force the IR memory address to be the dump_PC_reg -- on the next rising edge, -- the instruction on the output of the instruction memory should be given by -- this new PC (instead of the scan_PC_reg) go_dump_IR_mem <= '1'; -- If these become equal, then we are done dumping the instruction memory if ( dump_PC_reg = unsigned(scan_PC_reg) ) then state_next <= done; else state_next <= wait1; end if; -- ASCII OUTPUT: Two clk cycle latency on reads -- if you don't wait, you get -- zeros! when wait1 => go_dump_IR_mem <= '1'; state_next <= convert_BTA; -- --------------------------------------- -- ASCII OUTPUT: Convert the 12-bit binary value to 4 ascii bytes when convert_BTA => -- Keep this high because we may stall on BTA_ready and BTA doesn't -- take the snapshot until start_BTA_convert is asserted. go_dump_IR_mem <= '1'; -- Increment the dump_PC_reg -- start_BTA_convert will take a snapshot of the -- instruction memory output and carry out the conversion if ( BTA_ready = '1' ) then start_BTA_convert <= '1'; end if; -- Wait for conversion from binary to ASCII to finish if ( convert_BTA_done_tick = '1' ) then state_next <= send; dump_PC_next <= dump_PC_reg + 1; end if; -- --------------------------------------- -- ASCII OUTPUT: Load up the FIFO and let the transmitter do its work when send => if ( tx_full = '0' ) then cnt_next <= cnt_reg + 1; -- sign bit is first. if ( cnt_reg = 0 ) then send_one_byte <= '1'; ascii_send_data <= BTA_sign_char; end if; -- followed by 4 digits if ( cnt_reg = 1 ) then send_one_byte <= '1'; ascii_send_data <= R3; end if; if ( cnt_reg = 2 ) then send_one_byte <= '1'; ascii_send_data <= R2; end if; if ( cnt_reg = 3 ) then send_one_byte <= '1'; ascii_send_data <= R1; end if; if ( cnt_reg = 4 ) then send_one_byte <= '1'; ascii_send_data <= R0; end if; -- carriage return if ( cnt_reg = 5 ) then send_one_byte <= '1'; ascii_send_data <= std_logic_vector(to_unsigned(10, 8)); end if; -- line feed -- go back to dump_IR_mem state after this instruction is printed to the screen if ( cnt_reg = 6 ) then send_one_byte <= '1'; ascii_send_data <= std_logic_vector(to_unsigned(13, 8)); state_next <= dump_IR_mem; end if; end if; -- --------------------------------------- -- ASCII OUTPUT: Wait for the user to release the button when done => if (enter_pb_stable = '0') then state_next <= idle; end if; end case; end process; end beh;