📄 mc8051.vhd
字号:
-- When Port 0 is written to for data / addr purposes, the latch is reset -- to all ones. Process main is in charge of all writes to the memory. -- The following signal is used by get_pmem to indicate the reset: SIGNAL p0_reset : std_logic; -- Handshaking signal controlled by main to acknowledge above reset SIGNAL p0_reset_ack : std_logic; -- Denotes bad data read at s1p1 which could be an opcode SIGNAL bad_data : std_logic; -- Two signals that are used to resolve ale (from get_pmem and main) SIGNAL ale_pm, ale_dm : std_logic; -- Internal signals for port3 special functions SIGNAL wr_n_internal, rd_n_internal, rxd_internal, txd_internal, int0_n_internal, int1_n_internal, t0_internal, t1_internal : std_logic := '1'; -- the sbuf reg. maintained by the serial driver SIGNAL sbuf_dup : bvec; SIGNAL p2clk : std_logic; -- used by uart, high for any s?p2 SIGNAL addr_gb, data_gb : bvec; SIGNAL wr_gb, rd_gb : std_logic := '0'; SIGNAL acknow : std_logic; SIGNAL scon_out : bvec; ALIAS trans_int : std_logic IS scon_out(1); ALIAS recv_int : std_logic IS scon_out(0); SIGNAL TF1_t1, TF0_t0: std_ulogic := '0'; SIGNAL TF1, TF0, TF1_main, TF0_main, TF1_t0m3: std_ulogic := '0'; SIGNAL t0_in, t1_in: std_logic := '1'; SIGNAL TH1, TL1, TH0, TL0 : bvec := "00000000"; SIGNAL TH1_main, TL1_main, TH0_main, TL0_main: bvec := "00000000"; SIGNAL TH1_t1, TL1_t1, TH0_t0, TL0_t0: bvec := "00000000"; SIGNAL TH0_t0m3: bvec := "00000000"; SIGNAL interrupt_ack : std_logic := '0'; -- acknowledges one of the external interrupts - used for -- edge sensitive mode to reset the signal and wait for a -- new edge (edge sensitive based on IT0 and IT1 bits in TCON) SIGNAL current_priority : std_logic_vector(1 DOWNTO 0) := "00"; -- the current priority of the processor - used to determine -- if a 'higher' priority interrupt is indeed higher -- "00" lowest priority - any interrupt accepted -- "01" low priority - only high priority interrupts accepted -- "11" high priority - no interrupts accepted -- note, IE is also used to mask interrupts SIGNAL previous_priority : std_logic_vector(1 DOWNTO 0) := "00"; -- resolve2 resolves two signals into one -- Used for SFRs written to by processes other than main PROCEDURE resolve2 (signal first, second: in bvec; -- This transaction stuff is a hack to make the procedure fire signal first_trans, second_trans: in bit; signal output: out bvec) IS BEGIN if second'active then output <= second; end if; if first'active then output <= first; end if; END PROCEDURE resolve2; PROCEDURE resolve2 (signal first, second: in std_ulogic; -- This transaction stuff is a hack to make the procedure fire signal first_trans, second_trans: in bit; signal output: out std_ulogic) IS BEGIN if second'active then output <= second; end if; if first'active then output <= first; end if; END PROCEDURE resolve2;------------------------------------------------------------------------BEGIN -- architecture --=============================================================== -- Concurrent Signal Assignments --=============================================================== -- Strobe ale high whenever program or data memory requires it. ale <= 'Z' WHEN rst = '1' ELSE '1' WHEN (ale_pm = '1' OR ale_dm = '1') ELSE '0'; -- Put a weak low on control lines, so that a 1 write will pull high p0_ctrl <= 'L'; p2_ctrl <= 'L'; -- assign a high impedance version of the latch (either L or H) -- on any falling edge -- when the ctrl is asserted (by get_pmem or main) then -- force the addr/data value out the port P0 <= (OTHERS => 'Z') WHEN rst = '1' ELSE std_logic_vector(p0_addr) WHEN p0_ctrl = '1' ELSE std_logic_vector(to_high_imped(p0_latch)) WHEN cycle_state=s1p1 AND falling_edge(xtal1) ELSE UNAFFECTED; P1 <= (OTHERS => 'Z') WHEN rst = '1' ELSE std_logic_vector(to_high_imped(p1_latch)) WHEN cycle_state=s1p1 AND falling_edge(xtal1) ELSE UNAFFECTED; P2 <= (OTHERS => 'Z') WHEN rst = '1' ELSE std_logic_vector(p2_addr) WHEN p2_ctrl = '1' ELSE std_logic_vector(to_high_imped(p2_latch)) WHEN cycle_state=s1p1 AND falling_edge(xtal1) ELSE UNAFFECTED; P3 <= (OTHERS => 'Z') WHEN rst = '1' ELSE std_logic_vector(to_high_imped(p3_resolved)); -- Update the latch output at s1p1 p3_latch <= direct_hi_dmem(16#B0#) WHEN cycle_state=s1p1 AND falling_edge(xtal1) ELSE UNAFFECTED; -- Handle P3 special functions p3_resolved(7) <= rd_n_internal WHEN p3_latch(7) = '1' ELSE p3_latch(7); p3_resolved(6) <= wr_n_internal WHEN p3_latch(6) = '1' ELSE p3_latch(6); p3_resolved(5 downto 2) <= p3_latch(5 downto 2); p3_resolved(1) <= txd_internal WHEN p3_latch(1) = '1' ELSE p3_latch(1); p3_resolved(0) <= p3_latch(0); rxd_internal <= P3(0); -- whenever there is a falling edge on the interrupt pin, it will -- be sent to the interrupt processor (which may acknowledge or mask) -- for a level sensitive interrupt (it? = 0) the rising edge of the pin -- will clear out this internal signal. For an edge sensitive interrupt -- (it? = 1) the acknowledge from the interrupt processor will clear int0_n_internal <= '0' WHEN falling_edge(P3(2)) ELSE '0' WHEN P3(2) = '0' and it0 = '0' ELSE '1' WHEN rising_edge(P3(2)) AND it0 = '0' ELSE '1' WHEN interrupt_ack = '1' AND it0 = '1' ELSE int0_n_internal; int1_n_internal <= '0' WHEN falling_edge(P3(3)) ELSE '0' WHEN P3(3) = '0' and it1 = '0' ELSE '1' WHEN rising_edge(P3(3)) AND it1 = '0' ELSE '1' WHEN interrupt_ack = '1' AND it1 = '1' ELSE int1_n_internal; t0_internal <= P3(4); t1_internal <= P3(5); --=============================================================== -- Process Statements --=============================================================== ------------------------------------------------------------------------ -- The oscillator process will follow the XTAL clock signal and -- advance the current state. The states are s1p1, s1p2, s2p1, s2p2, -- up to s6p1 and s6p2. ------------------------------------------------------------------------ oscillator : PROCESS (XTAL1) IS VARIABLE last_falling_edge_time : TIME := 0 ns; VARIABLE startup_count : INTEGER := 0; BEGIN IF falling_edge(XTAL1) THEN IF startup_count < 3 THEN cycle_state <= init; startup_count := startup_count + 1; last_falling_edge_time := NOW; ELSE cycle_state <= inc(cycle_state); -- increment the current state, -- and loop back from s6p2 to s1p1 TCLCL <= NOW - last_falling_edge_time; last_falling_edge_time := NOW; END IF; END IF; END PROCESS oscillator; ------------------------------------------------------------------------ -- The process get_pmem is responsible for reading all of the program -- memory (whetere internal or external) and feeding the read bytes to -- the process main through the signals pmem_s1_byte and pmem_s4_byte. -- If there has been a transaction on pc, this process will set -- its internal addr to the value of pc at state s4p1. Then, if needed, -- it will output this addr at s5p1 and then read in the appropriate data -- at s1p1. Then it increments its internal addr and will read the next -- byte at s4p1. It will continue to increment the internal addr until -- there is another transaction on pc. ------------------------------------------------------------------------- get_pmem : PROCESS(cycle_state, pc'TRANSACTION) IS VARIABLE addr : INTEGER := 0; VARIABLE pmem : program_mem_T; -- the program memory VARIABLE prog_loaded : BOOLEAN := FALSE; -- true after pmem is updated VARIABLE resync : BOOLEAN := FALSE; -- set true when pc changes VARIABLE port_to_01 : bit_vector(7 DOWNTO 0); BEGIN -- Put lines at HiZ if the processor is in reset state IF rst = '1' THEN psen_n <= 'Z'; ale_pm <= 'Z'; p0_ctrl <= 'Z'; p2_ctrl <= 'Z'; p0_addr <= "ZZZZZZZZ"; p2_addr <= "ZZZZZZZZ"; ELSIF NOT prog_loaded THEN IF (ea_n = '1' or ea_n = 'H') THEN load_program(program_filename,pmem); END IF; -- Set default values for control lines psen_n <= '1'; p0_ctrl <= 'Z'; p2_ctrl <= 'Z'; p0_reset <= '0'; prog_loaded := TRUE; ELSE -- If process main has acknowledged a reset, then -- clear the p0 reset line. IF p0_reset_ack <= '1' THEN p0_reset <= '0'; END IF; -- If there has been a transaction on pc, and it is -- not the initial start-up state, then flag the -- resync variable IF pc'ACTIVE THEN resync := TRUE; END IF; -- If the process main is trying to read a data byte -- from external mem, yield to it by putting ctrl's -- to high impedance IF port_req = '1' THEN p0_ctrl <= 'Z'; p2_ctrl <= 'Z'; p0_addr <= "ZZZZZZZZ"; p2_addr <= "ZZZZZZZZ"; ale_pm <= '0'; psen_n <= '1'; ELSIF reset_pmem = '1' THEN resync := TRUE; addr := 0; ELSIF cycle_state'ACTIVE THEN CASE cycle_state IS WHEN init => NULL; WHEN s1p1 => -- read in the current byte from pmem IF ( addr > 16#0FFF#) OR (ea_n = '0') OR (ea_n = 'L') THEN IF Is_X(P0) THEN pmem_s1_byte <= unsigned'("00000000"); bad_data <= '1'; ELSE port_to_01 := to_bitVector(P0); pmem_s1_byte <= unsigned(to_stdlogicvector(port_to_01)); bad_data <= '0'; END IF; ELSE -- fetch from internal memory pmem_s1_byte <= pmem(addr); END IF; WHEN s4p1 => -- read in the current byte from pmem IF ( addr >= 16#0FFF#) OR (ea_n = '0') OR (ea_n = 'L') THEN port_to_01 := to_bitVector(P0); pmem_s4_byte <= unsigned(to_stdlogicvector(port_to_01)); ELSE -- fetch from internal memory pmem_s4_byte <= pmem(addr); END IF; WHEN s1p2 | s4p2 => IF resync THEN addr := conv_integer(pc); resync := FALSE; ELSE addr := addr + 1; END IF; -- strobe ale if next addr is external -- rewrite p0_latch to all 1's IF ( addr >= 16#0FFF#) OR (ea_n = '0') OR (ea_n = 'L') THEN ale_pm <= '1'; psen_n <= '1'; p0_reset <= '1'; END IF; WHEN s2p1 | s5p1 => -- drive port 0 and port 2 if addr is external IF ( addr >= 16#0FFF#) OR (ea_n = '0') OR (ea_n = 'L') THEN p0_addr <= conv_unsigned(addr MOD 256, 8); p2_addr <= conv_unsigned(addr / 256, 8); p0_ctrl <= '1'; p2_ctrl <= '1'; ELSE p0_ctrl <= 'Z'; p2_ctrl <= 'Z'; END IF; WHEN s2p2 | s5p2 => -- drive ale to zero IF ( addr >= 16#0FFF#) OR (ea_n = '0') OR (ea_n = 'L') THEN ale_pm <= '0'; END IF; WHEN s3p1 | s6p1 => -- drive psen low IF ( addr >= 16#0FFF#) OR (ea_n = '0') OR (ea_n = 'L') THEN psen_n <= '0'; p0_addr <= "ZZZZZZZZ"; END IF; WHEN s3p2 | s6p2 => NULL; END CASE; END IF; END IF;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -