mirror of
https://github.com/Gehstock/Mist_FPGA.git
synced 2026-04-26 12:27:17 +00:00
1075 lines
33 KiB
Verilog
1075 lines
33 KiB
Verilog
//
|
|
// A simulation model of VIC20 hardware - VIA implementation
|
|
// Copyright (c) MikeJ - March 2003
|
|
//
|
|
// All rights reserved
|
|
//
|
|
// Redistribution and use in source and synthezised forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
//
|
|
// Redistributions in synthesized form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
//
|
|
// Neither the name of the author nor the names of other contributors may
|
|
// be used to endorse or promote products derived from this software without
|
|
// specific prior written permission.
|
|
//
|
|
// THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// You are responsible for any legal issues arising from your use of this code.
|
|
//
|
|
// The latest version of this file can be found at: www.fpgaarcade.com
|
|
//
|
|
// Email vic20@fpgaarcade.com
|
|
//
|
|
//
|
|
// Revision list
|
|
//
|
|
// version 005 Many fixes to all areas, VIA now passes all VICE tests
|
|
// version 004 fixes to PB7 T1 control and Mode 0 Shift Register operation
|
|
// version 003 fix reset of T1/T2 IFR flags if T1/T2 is reload via reg5/reg9 from wolfgang (WoS)
|
|
// Ported to numeric_std and simulation fix for signal initializations from arnim laeuger
|
|
// version 002 fix from Mark McDougall, untested
|
|
// version 001 initial release
|
|
// not very sure about the shift register, documentation is a bit light.
|
|
|
|
module m6522
|
|
(
|
|
input [3:0] I_RS ,
|
|
input [7:0] I_DATA ,
|
|
output reg [7:0] O_DATA ,
|
|
output O_DATA_OE_L ,
|
|
|
|
input I_RW_L ,
|
|
input I_CS1 ,
|
|
input I_CS2_L ,
|
|
|
|
output O_IRQ_L , // note, not open drain
|
|
|
|
// port a
|
|
input I_CA1 ,
|
|
input I_CA2 ,
|
|
output reg O_CA2 ,
|
|
output reg O_CA2_OE_L ,
|
|
|
|
input [7:0] I_PA ,
|
|
output [7:0] O_PA ,
|
|
output [7:0] O_PA_OE_L ,
|
|
|
|
// port b
|
|
input I_CB1 ,
|
|
output O_CB1 ,
|
|
output O_CB1_OE_L ,
|
|
|
|
input I_CB2 ,
|
|
output reg O_CB2 ,
|
|
output reg O_CB2_OE_L ,
|
|
|
|
input [7:0] I_PB ,
|
|
output [7:0] O_PB ,
|
|
output [7:0] O_PB_OE_L ,
|
|
|
|
input I_P2_H , // high for phase 2 clock ____////__
|
|
input RESET_L ,
|
|
input ENA_4 , // clk enable
|
|
input CLK
|
|
);
|
|
|
|
|
|
|
|
reg [1:0] phase = 2'b00;
|
|
reg p2_h_t1;
|
|
wire cs;
|
|
|
|
// registers
|
|
reg [7:0] r_ddra;
|
|
reg [7:0] r_ora;
|
|
reg [7:0] r_ira;
|
|
|
|
reg [7:0] r_ddrb;
|
|
reg [7:0] r_orb;
|
|
reg [7:0] r_irb;
|
|
|
|
reg [7:0] r_t1l_l;
|
|
reg [7:0] r_t1l_h;
|
|
reg [7:0] r_t2l_l;
|
|
reg [7:0] r_t2l_h; // not in real chip
|
|
reg [7:0] r_sr;
|
|
reg [7:0] r_acr;
|
|
reg [7:0] r_pcr;
|
|
wire [7:0] r_ifr;
|
|
reg [6:0] r_ier;
|
|
|
|
reg sr_write_ena;
|
|
reg sr_read_ena;
|
|
reg ifr_write_ena;
|
|
reg ier_write_ena;
|
|
wire [7:0] clear_irq;
|
|
reg [7:0] load_data;
|
|
|
|
// timer 1
|
|
reg [15:0] t1c = 16'hffff; // simulators may not catch up w/o init here...
|
|
reg t1c_active;
|
|
reg t1c_done;
|
|
reg t1_w_reset_int;
|
|
reg t1_r_reset_int;
|
|
reg t1_load_counter;
|
|
reg t1_reload_counter;
|
|
reg t1_int_enable = 1'b0;
|
|
reg t1_toggle;
|
|
reg t1_irq = 1'b0;
|
|
reg t1_pb7 = 1'b1;
|
|
reg t1_pb7_en_c;
|
|
reg t1_pb7_en_d;
|
|
|
|
// timer 2
|
|
reg [15:0] t2c = 16'hffff; // simulators may not catch up w/o init here...
|
|
reg t2c_active;
|
|
reg t2c_done;
|
|
reg t2_pb6;
|
|
reg t2_pb6_t1;
|
|
reg t2_cnt_clk = 1'b1;
|
|
reg t2_w_reset_int;
|
|
reg t2_r_reset_int;
|
|
reg t2_load_counter;
|
|
reg t2_reload_counter;
|
|
reg t2_int_enable = 1'b0;
|
|
reg t2_irq = 1'b0;
|
|
reg t2_sr_ena;
|
|
|
|
// shift reg
|
|
reg [3:0] sr_cnt;
|
|
reg sr_cb1_oe_l;
|
|
reg sr_cb1_out;
|
|
reg sr_drive_cb2;
|
|
reg sr_strobe;
|
|
reg sr_do_shift = 1'b0;
|
|
reg sr_strobe_t1;
|
|
reg sr_strobe_falling;
|
|
reg sr_strobe_rising;
|
|
reg sr_irq;
|
|
reg sr_out;
|
|
reg sr_active;
|
|
|
|
// io
|
|
reg w_orb_hs;
|
|
reg w_ora_hs;
|
|
reg r_irb_hs;
|
|
reg r_ira_hs;
|
|
|
|
reg ca_hs_sr;
|
|
reg ca_hs_pulse;
|
|
reg cb_hs_sr;
|
|
reg cb_hs_pulse;
|
|
|
|
wire cb1_in_mux;
|
|
reg ca1_ip_reg_c;
|
|
reg ca1_ip_reg_d;
|
|
reg cb1_ip_reg_c;
|
|
reg cb1_ip_reg_d;
|
|
wire ca1_int;
|
|
wire cb1_int;
|
|
reg ca1_irq;
|
|
reg cb1_irq;
|
|
|
|
reg ca2_ip_reg_c;
|
|
reg ca2_ip_reg_d;
|
|
reg cb2_ip_reg_c;
|
|
reg cb2_ip_reg_d;
|
|
wire ca2_int;
|
|
wire cb2_int;
|
|
reg ca2_irq;
|
|
reg cb2_irq;
|
|
|
|
reg final_irq;
|
|
|
|
|
|
always @(posedge CLK) begin
|
|
if (ENA_4 == 1'b1) begin
|
|
p2_h_t1 <= I_P2_H;
|
|
if ((p2_h_t1 == 1'b0) & (I_P2_H == 1'b1)) begin
|
|
phase <= 2'b11;
|
|
end
|
|
else begin
|
|
phase <= phase + 1'b1;
|
|
end
|
|
end
|
|
end
|
|
|
|
// internal clock phase
|
|
assign cs = (I_CS1 == 1'b1 & I_CS2_L == 1'b0 & I_P2_H == 1'b1) ? 1'b1 : 1'b0;
|
|
|
|
// peripheral control reg (pcr)
|
|
// 0 ca1 interrupt control (0 +ve edge, 1 -ve edge)
|
|
// 3..1 ca2 operation
|
|
// 000 input -ve edge
|
|
// 001 independend interrupt input -ve edge
|
|
// 010 input +ve edge
|
|
// 011 independend interrupt input +ve edge
|
|
// 100 handshake output
|
|
// 101 pulse output
|
|
// 110 low output
|
|
// 111 high output
|
|
// 7..4 as 3..0 for cb1,cb2
|
|
|
|
// auxiliary control reg (acr)
|
|
// 0 input latch PA (0 disable, 1 enable)
|
|
// 1 input latch PB (0 disable, 1 enable)
|
|
// 4..2 shift reg control
|
|
// 000 disable
|
|
// 001 shift in using t2
|
|
// 010 shift in using o2
|
|
// 011 shift in using ext clk
|
|
// 100 shift out free running t2 rate
|
|
// 101 shift out using t2
|
|
// 101 shift out using o2
|
|
// 101 shift out using ext clk
|
|
// 5 t2 timer control (0 timed interrupt, 1 count down with pulses on pb6)
|
|
// 7..6 t1 timer control
|
|
// 00 timed interrupt each time t1 is loaded pb7 disable
|
|
// 01 continuous interrupts pb7 disable
|
|
// 00 timed interrupt each time t1 is loaded pb7 one shot output
|
|
// 01 continuous interrupts pb7 square wave output
|
|
//
|
|
|
|
always @(posedge CLK, negedge RESET_L) begin
|
|
if (RESET_L == 1'b0) begin
|
|
r_ora <= 8'h00;
|
|
r_orb <= 8'h00;
|
|
r_ddra <= 8'h00;
|
|
r_ddrb <= 8'h00;
|
|
r_acr <= 8'h00;
|
|
r_pcr <= 8'h00;
|
|
w_orb_hs <= 1'b0;
|
|
w_ora_hs <= 1'b0;
|
|
end else begin
|
|
if (ENA_4 == 1'b1) begin
|
|
w_orb_hs <= 1'b0;
|
|
w_ora_hs <= 1'b0;
|
|
if ((cs == 1'b1) & (I_RW_L == 1'b0)) begin
|
|
case (I_RS)
|
|
4'h 0 : begin
|
|
r_orb <= I_DATA;
|
|
w_orb_hs <= 1'b1;
|
|
end
|
|
|
|
4'h 1 : begin
|
|
r_ora <= I_DATA;
|
|
w_ora_hs <= 1'b1;
|
|
end
|
|
|
|
4'h 2 : begin
|
|
r_ddrb <= I_DATA;
|
|
end
|
|
|
|
4'h 3 : begin
|
|
r_ddra <= I_DATA;
|
|
end
|
|
|
|
|
|
4'h B : begin
|
|
r_acr <= I_DATA;
|
|
end
|
|
|
|
4'h C : begin
|
|
r_pcr <= I_DATA;
|
|
end
|
|
|
|
4'h F : begin
|
|
r_ora <= I_DATA;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
// Set timer PB7 state, only on rising edge of setting ACR(7)
|
|
if ((t1_pb7_en_d == 1'b0) & (t1_pb7_en_c == 1'b1)) begin
|
|
t1_pb7 <= 1'b1;
|
|
end
|
|
|
|
if (t1_load_counter) begin
|
|
t1_pb7 <= 1'b0; // Reset internal timer 1 PB7 state on every timer load
|
|
end else if (t1_toggle == 1'b1) begin
|
|
t1_pb7 <= !t1_pb7;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
always @(posedge CLK, negedge RESET_L) begin
|
|
if (RESET_L == 1'b0) begin
|
|
// The spec says, this is not reset.
|
|
// Fact is that the 1541 VIA1 timer won't work,
|
|
// as the firmware ONLY sets the r_t1l_h latch!!!!
|
|
r_t1l_l <= 8'hff; // All latches default to FFFF
|
|
r_t1l_h <= 8'hff;
|
|
r_t2l_l <= 8'hff;
|
|
r_t2l_h <= 8'hff;
|
|
end else begin
|
|
if (ENA_4 == 1'b1) begin
|
|
t1_w_reset_int <= 1'b0;
|
|
t1_load_counter <= 1'b0;
|
|
|
|
t2_w_reset_int <= 1'b0;
|
|
t2_load_counter <= 1'b0;
|
|
|
|
load_data <= 8'h00;
|
|
sr_write_ena <= 1'b0;
|
|
ifr_write_ena <= 1'b0;
|
|
ier_write_ena <= 1'b0;
|
|
|
|
if ((cs == 1'b1) & (I_RW_L == 1'b0)) begin
|
|
load_data <= I_DATA;
|
|
case (I_RS)
|
|
4'h4: begin
|
|
r_t1l_l <= I_DATA;
|
|
end
|
|
|
|
4'h5: begin
|
|
r_t1l_h <= I_DATA;
|
|
t1_w_reset_int <= 1'b1;
|
|
t1_load_counter <= 1'b1;
|
|
end
|
|
|
|
4'h6: begin
|
|
r_t1l_l <= I_DATA;
|
|
end
|
|
|
|
4'h7: begin
|
|
r_t1l_h <= I_DATA;
|
|
t1_w_reset_int <= 1'b1;
|
|
end
|
|
|
|
|
|
4'h8: begin
|
|
r_t2l_l <= I_DATA;
|
|
end
|
|
|
|
4'h9: begin
|
|
r_t2l_h <= I_DATA;
|
|
t2_w_reset_int <= 1'b1;
|
|
t2_load_counter <= 1'b1;
|
|
end
|
|
|
|
4'hA: begin
|
|
sr_write_ena <= 1'b1;
|
|
end
|
|
|
|
4'hD: begin
|
|
ifr_write_ena <= 1'b1;
|
|
end
|
|
|
|
4'hE: begin
|
|
ier_write_ena <= 1'b1;
|
|
end
|
|
|
|
endcase
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
assign O_DATA_OE_L = (cs == 1'b1 & I_RW_L == 1'b 1) ? 1'b0 : 1'b1;
|
|
|
|
reg [7:0] orb;
|
|
|
|
always @(*) begin
|
|
t1_r_reset_int <= 1'b0;
|
|
t2_r_reset_int <= 1'b0;
|
|
sr_read_ena <= 1'b0;
|
|
r_irb_hs <= 1'b0;
|
|
r_ira_hs <= 1'b0;
|
|
O_DATA <= 8'h00; // default
|
|
|
|
orb = (r_irb & !r_ddrb) | (r_orb & r_ddrb);
|
|
|
|
// If PB7 under timer control, assign value from timer
|
|
if (t1_pb7_en_d == 1'b1) begin
|
|
orb[7] = t1_pb7;
|
|
end
|
|
|
|
if ((cs == 1'b1) & (I_RW_L == 1'b1)) begin
|
|
case (I_RS)
|
|
4'h0: begin
|
|
O_DATA <= orb; r_irb_hs <= 1'b1;
|
|
end
|
|
4'h1: begin
|
|
O_DATA <= (r_ira & !r_ddra) | (r_ora & r_ddra);
|
|
r_ira_hs <= 1'b1;
|
|
end
|
|
4'h2: begin
|
|
O_DATA <= r_ddrb;
|
|
end
|
|
4'h3: begin
|
|
O_DATA <= r_ddra;
|
|
end
|
|
4'h4: begin
|
|
O_DATA <= t1c[7:0];
|
|
t1_r_reset_int <= 1'b1;
|
|
end
|
|
4'h5: begin
|
|
O_DATA <= t1c[15:8];
|
|
end
|
|
4'h6: begin
|
|
O_DATA <= r_t1l_l;
|
|
end
|
|
4'h7: begin
|
|
O_DATA <= r_t1l_h;
|
|
end
|
|
4'h8: begin
|
|
O_DATA <= t2c[7:0];
|
|
t2_r_reset_int <= 1'b1;
|
|
end
|
|
4'h9: begin
|
|
O_DATA <= t2c[15:8];
|
|
end
|
|
4'hA: begin
|
|
O_DATA <= r_sr;
|
|
sr_read_ena <= 1'b1;
|
|
end
|
|
4'hB: begin
|
|
O_DATA <= r_acr;
|
|
end
|
|
4'hC: begin
|
|
O_DATA <= r_pcr;
|
|
end
|
|
4'hD: begin
|
|
O_DATA <= r_ifr;
|
|
end
|
|
4'hE: begin
|
|
O_DATA <= (1'b0 & r_ier);
|
|
end
|
|
4'hF: begin
|
|
O_DATA <= r_ira;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
end
|
|
|
|
//
|
|
// IO
|
|
//
|
|
|
|
|
|
// if the shift register is enabled, cb1 may be an output
|
|
// in this case we should NOT listen to the input as
|
|
// CB1 interrupts are not generated by the shift register
|
|
|
|
assign cb1_in_mux = (sr_cb1_oe_l === 1'b 1) ? I_CB1 : 1'b1;
|
|
|
|
// ca1 control
|
|
assign ca1_int = (r_pcr[0] == 1'b0) ?
|
|
// negative edge
|
|
(ca1_ip_reg_d == 1'b1) & (ca1_ip_reg_c == 1'b0) :
|
|
// positive edge
|
|
(ca1_ip_reg_d == 1'b0) & (ca1_ip_reg_c == 1'b1);
|
|
|
|
// cb1 control
|
|
assign cb1_int = (r_pcr[4] == 1'b0) ?
|
|
// negative edge
|
|
(cb1_ip_reg_d == 1'b1) & (cb1_ip_reg_c == 1'b0) :
|
|
// positive edge
|
|
(cb1_ip_reg_d == 1'b0) & (cb1_ip_reg_c == 1'b1);
|
|
|
|
|
|
assign ca2_int = (r_pcr[3] == 1'b1) ?
|
|
// ca2 input
|
|
1'b0 :
|
|
(r_pcr[2] == 1'b0) ?
|
|
// ca2 negative edge
|
|
(ca2_ip_reg_d == 1'b1) & (ca2_ip_reg_c == 1'b0) :
|
|
// ca2 positive edge
|
|
(ca2_ip_reg_d == 1'b0) & (ca2_ip_reg_c == 1'b1);
|
|
|
|
|
|
assign cb2_int = (r_pcr[7] == 1'b1) ?
|
|
// cb2 input
|
|
1'b0 :
|
|
(r_pcr[6] == 1'b0) ?
|
|
// cb2 negative edge
|
|
(cb2_ip_reg_d == 1'b1) & (cb2_ip_reg_c == 1'b0) :
|
|
// cb2 positive edge
|
|
(cb2_ip_reg_d == 1'b0) & (cb2_ip_reg_c == 1'b1);
|
|
|
|
|
|
|
|
|
|
|
|
always @(posedge CLK, negedge RESET_L) begin
|
|
if (RESET_L == 1'b0) begin
|
|
O_CA2 <= 1'b1; // Pullup is default
|
|
O_CA2_OE_L <= 1'b1;
|
|
O_CB2 <= 1'b1; // Pullup is default
|
|
O_CB2_OE_L <= 1'b1;
|
|
|
|
ca_hs_sr <= 1'b0;
|
|
ca_hs_pulse <= 1'b0;
|
|
cb_hs_sr <= 1'b0;
|
|
cb_hs_pulse <= 1'b0;
|
|
end else begin
|
|
if (ENA_4 == 1'b1) begin
|
|
// ca
|
|
if ((phase == 2'b00) & ((w_ora_hs == 1'b1) | (r_ira_hs == 1'b1))) begin
|
|
ca_hs_sr <= 1'b1;
|
|
end else if (ca1_int) begin
|
|
ca_hs_sr <= 1'b0;
|
|
end
|
|
|
|
if (phase == 2'b00) begin
|
|
ca_hs_pulse <= w_ora_hs | r_ira_hs;
|
|
end
|
|
|
|
O_CA2_OE_L <= !r_pcr[3]; // ca2 output
|
|
case (r_pcr[3:1])
|
|
3'b000: begin
|
|
O_CA2 <= I_CA2; // input, output follows input
|
|
end
|
|
3'b001: begin
|
|
O_CA2 <= I_CA2; // input, output follows input
|
|
end
|
|
3'b010: begin
|
|
O_CA2 <= I_CA2; // input, output follows input
|
|
end
|
|
3'b011: begin
|
|
O_CA2 <= I_CA2; // input, output follows input
|
|
end
|
|
3'b100: begin
|
|
O_CA2 <= !(ca_hs_sr); // handshake
|
|
end
|
|
3'b101: begin
|
|
O_CA2 <= !(ca_hs_pulse); // pulse
|
|
end
|
|
3'b110: begin
|
|
O_CA2 <= 1'b0; // low
|
|
end
|
|
3'b111: begin
|
|
O_CA2 <= 1'b1; // high
|
|
end
|
|
endcase
|
|
|
|
if ((phase == 2'b00) & (w_orb_hs == 1'b1)) begin
|
|
cb_hs_sr <= 1'b1;
|
|
end else if (cb1_int) begin
|
|
cb_hs_sr <= 1'b0;
|
|
end
|
|
|
|
if (phase == 2'b00) begin
|
|
cb_hs_pulse <= w_orb_hs;
|
|
end
|
|
|
|
O_CB2_OE_L <= !(r_pcr[7] | sr_drive_cb2); // cb2 output or serial
|
|
if (sr_drive_cb2 == 1'b1) begin // serial output
|
|
O_CB2 <= sr_out;
|
|
end
|
|
else begin
|
|
case (r_pcr[7:5])
|
|
3'b000: begin
|
|
O_CB2 <= I_CB2; // input, output follows input
|
|
end
|
|
3'b001: begin
|
|
O_CB2 <= I_CB2; // input, output follows input
|
|
end
|
|
3'b010: begin
|
|
O_CB2 <= I_CB2; // input, output follows input
|
|
end
|
|
3'b011: begin
|
|
O_CB2 <= I_CB2; // input, output follows input
|
|
end
|
|
3'b100: begin
|
|
O_CB2 <= !(cb_hs_sr); // handshake
|
|
end
|
|
3'b101: begin
|
|
O_CB2 <= !(cb_hs_pulse); // pulse
|
|
end
|
|
3'b110: begin
|
|
O_CB2 <= 1'b0; // low
|
|
end
|
|
3'b111: begin
|
|
O_CB2 <= 1'b1; // high
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
assign O_CB1 = sr_cb1_out;
|
|
assign O_CB1_OE_L = sr_cb1_oe_l;
|
|
|
|
always @(posedge CLK, negedge RESET_L) begin
|
|
if (RESET_L == 1'b0) begin
|
|
ca1_irq <= 1'b0;
|
|
ca2_irq <= 1'b0;
|
|
cb1_irq <= 1'b0;
|
|
cb2_irq <= 1'b0;
|
|
end else begin
|
|
if (ENA_4 == 1'b1) begin
|
|
// not pretty
|
|
if (ca1_int) begin
|
|
ca1_irq <= 1'b1;
|
|
end else if ((r_ira_hs == 1'b1) | (w_ora_hs == 1'b1) | (clear_irq[1] == 1'b1)) begin
|
|
ca1_irq <= 1'b0;
|
|
end
|
|
|
|
if (ca2_int) begin
|
|
ca2_irq <= 1'b1;
|
|
end
|
|
else begin
|
|
if ((((r_ira_hs == 1'b1) | (w_ora_hs == 1'b1)) & (r_pcr[1] == 1'b0)) | (clear_irq[0] == 1'b1)) begin
|
|
ca2_irq <= 1'b0;
|
|
end
|
|
end
|
|
|
|
if (cb1_int) begin
|
|
cb1_irq <= 1'b1;
|
|
end else if ((r_irb_hs == 1'b1) | (w_orb_hs == 1'b1) | (clear_irq[4] == 1'b1)) begin
|
|
cb1_irq <= 1'b0;
|
|
end
|
|
|
|
if (cb2_int) begin
|
|
cb2_irq <= 1'b1;
|
|
end
|
|
else begin
|
|
if ((((r_irb_hs == 1'b1) | (w_orb_hs == 1'b1)) & (r_pcr[5] == 1'b0)) | (clear_irq[3] == 1'b1)) begin
|
|
cb2_irq <= 1'b0;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
always @(posedge CLK, negedge RESET_L) begin
|
|
if (RESET_L == 1'b0) begin
|
|
ca1_ip_reg_c <= 1'b0;
|
|
ca1_ip_reg_d <= 1'b0;
|
|
|
|
cb1_ip_reg_c <= 1'b0;
|
|
cb1_ip_reg_d <= 1'b0;
|
|
|
|
ca2_ip_reg_c <= 1'b0;
|
|
ca2_ip_reg_d <= 1'b0;
|
|
|
|
cb2_ip_reg_c <= 1'b0;
|
|
cb2_ip_reg_d <= 1'b0;
|
|
|
|
r_ira <= 8'h00;
|
|
r_irb <= 8'h00;
|
|
|
|
end else begin
|
|
if (ENA_4 == 1'b1) begin
|
|
// we have a fast clock, so we can have input registers
|
|
ca1_ip_reg_c <= I_CA1;
|
|
ca1_ip_reg_d <= ca1_ip_reg_c;
|
|
|
|
cb1_ip_reg_c <= cb1_in_mux;
|
|
cb1_ip_reg_d <= cb1_ip_reg_c;
|
|
|
|
ca2_ip_reg_c <= I_CA2;
|
|
ca2_ip_reg_d <= ca2_ip_reg_c;
|
|
|
|
cb2_ip_reg_c <= I_CB2;
|
|
cb2_ip_reg_d <= cb2_ip_reg_c;
|
|
|
|
if (r_acr[0] == 1'b0) begin
|
|
r_ira <= I_PA;
|
|
end
|
|
else begin // enable latching
|
|
if (ca1_int) begin
|
|
r_ira <= I_PA;
|
|
end
|
|
end
|
|
|
|
if (r_acr[1] == 1'b0) begin
|
|
r_irb <= I_PB;
|
|
end
|
|
else begin // enable latching
|
|
if (cb1_int) begin
|
|
r_irb <= I_PB;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// data direction reg (ddr) 0 = input, 1 = output
|
|
assign O_PA = r_ora;
|
|
assign O_PA_OE_L = ~r_ddra;
|
|
|
|
// If PB7 is timer driven output set PB7 to the timer state, otherwise use value in ORB register
|
|
assign O_PB = (t1_pb7_en_d == 1'b1) ? { t1_pb7 , r_orb[6:0]} : r_orb;
|
|
|
|
// NOTE: r_ddrb(7) must be set to enable T1 output on PB7 - [various datasheets specify this]
|
|
assign O_PB_OE_L = ~r_ddrb;
|
|
|
|
//
|
|
// Timer 1
|
|
//
|
|
|
|
// Detect change in r_acr(7), timer 1 mode for PB7
|
|
always @(posedge CLK) begin
|
|
if (ENA_4 == 1'b1) begin
|
|
t1_pb7_en_c <= r_acr[7];
|
|
t1_pb7_en_d <= t1_pb7_en_c;
|
|
end
|
|
end
|
|
|
|
reg p_timer1_done;
|
|
always @(posedge CLK) begin
|
|
if (ENA_4 == 1'b1) begin
|
|
p_timer1_done = (t1c == 16'h0000);
|
|
t1c_done <= p_timer1_done & (phase == 2'b11);
|
|
if ((phase == 2'b11) & !t1_load_counter) begin // Don't set reload if T1L-H written
|
|
t1_reload_counter <= p_timer1_done;
|
|
end else if (t1_load_counter) begin // Cancel a reload when T1L-H written
|
|
t1_reload_counter <= 1'b0;
|
|
end
|
|
if (t1_load_counter) begin // done reset on load!
|
|
t1c_done <= 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
always @(posedge CLK) begin
|
|
if (ENA_4 == 1'b1) begin
|
|
if (t1_load_counter | (t1_reload_counter & phase == 2'b11)) begin
|
|
t1c[ 7:0] <= r_t1l_l;
|
|
t1c[15:8] <= r_t1l_h;
|
|
// There is a need to write to Latch HI to enable interrupts for both continuous and one-shot modes
|
|
if (t1_load_counter) begin
|
|
t1_int_enable <= 1'b1;
|
|
end
|
|
end else if (phase == 2'b11) begin
|
|
t1c <= t1c - 1'b1;
|
|
end
|
|
|
|
if (t1_load_counter | t1_reload_counter) begin
|
|
t1c_active <= 1'b1;
|
|
end else if (t1c_done) begin
|
|
t1c_active <= 1'b0;
|
|
end
|
|
|
|
t1_toggle <= 1'b0;
|
|
if (t1c_active & t1c_done) begin
|
|
if (t1_int_enable) begin // Set interrupt only if T1L-H has been written
|
|
t1_toggle <= 1'b1;
|
|
t1_irq <= 1'b1;
|
|
if (r_acr[6] == 1'b0) begin // Disable further interrupts if in one shot mode
|
|
t1_int_enable <= 1'b0;
|
|
end
|
|
end
|
|
end else if (t1_w_reset_int | t1_r_reset_int | (clear_irq[6] == 1'b1)) begin
|
|
t1_irq <= 1'b0;
|
|
end
|
|
if (t1_load_counter) begin // irq reset on load!
|
|
t1_irq <= 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
//
|
|
// Timer2
|
|
//
|
|
|
|
always @(posedge CLK) begin
|
|
if (ENA_4 == 1'b1) begin
|
|
if (phase == 2'b01) begin // leading edge p2_h
|
|
t2_pb6 <= I_PB[6];
|
|
t2_pb6_t1 <= t2_pb6;
|
|
end
|
|
end
|
|
end
|
|
|
|
// Ensure we don't start counting until the P2 clock after r_acr is changed
|
|
always @(posedge I_P2_H) begin
|
|
if (r_acr[5] == 1'b0) begin
|
|
t2_cnt_clk <= 1'b1;
|
|
end
|
|
else begin
|
|
t2_cnt_clk <= 1'b0;
|
|
end
|
|
end
|
|
|
|
reg p_timer2_done;
|
|
reg p_timer2_done_sr;
|
|
always @(posedge CLK) begin
|
|
if (ENA_4 == 1'b1) begin
|
|
p_timer2_done = (t2c == 16'h0000); // Normal timer expires at 0000
|
|
p_timer2_done_sr = (t2c[7:0] == 8'h00); // Shift register expires on low byte == 00
|
|
t2c_done <= p_timer2_done & (phase == 2'b11);
|
|
if (phase == 2'b11) begin
|
|
t2_reload_counter <= p_timer2_done_sr; // Timer 2 is only reloaded when used for the shift register
|
|
end
|
|
if (t2_load_counter) begin // done reset on load!
|
|
t2c_done <= 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
reg p_timer2_ena;
|
|
always @(posedge CLK) begin
|
|
if (ENA_4 == 1'b1) begin
|
|
if (t2_cnt_clk == 1'b1) begin
|
|
p_timer2_ena = 1'b1;
|
|
t2c_active <= 1'b1;
|
|
t2_int_enable <= 1'b1;
|
|
end
|
|
else begin
|
|
p_timer2_ena = (t2_pb6_t1 == 1'b1) & (t2_pb6 == 1'b0); // falling edge
|
|
end
|
|
|
|
// Shift register reload is only active when shift register mode using T2 is enabled
|
|
if (t2_reload_counter & (phase == 2'b11) & ((r_acr[4:2] == 3'b001) | (r_acr[4:2] == 3'b100) | (r_acr[4:2] == 3'b101))) begin
|
|
t2c[7:0] <= r_t2l_l; // For shift register only low latch is loaded!
|
|
end else if (t2_load_counter) begin
|
|
t2_int_enable <= 1'b1;
|
|
t2c[ 7:0] <= r_t2l_l;
|
|
t2c[15:8] <= r_t2l_h;
|
|
end
|
|
else begin
|
|
if ((phase == 2'b11) & p_timer2_ena) begin // or count mode
|
|
t2c <= t2c - 1'b1;
|
|
end
|
|
end
|
|
|
|
// Shift register strobe on T2 occurs one P2H clock after timer expires
|
|
// so enable the strobe when we roll over to FF
|
|
t2_sr_ena <= (t2c[7:0] == 8'hFF) & (phase == 2'b11);
|
|
|
|
if (t2_load_counter) begin
|
|
t2c_active <= 1'b1;
|
|
end else if (t2c_done) begin
|
|
t2c_active <= 1'b0;
|
|
end
|
|
|
|
if (t2c_active & t2c_done & t2_int_enable) begin
|
|
t2_int_enable <= 1'b0;
|
|
t2_irq <= 1'b1;
|
|
end else if (t2_w_reset_int | t2_r_reset_int | (clear_irq[5] == 1'b1)) begin
|
|
t2_irq <= 1'b0;
|
|
end
|
|
if (t2_load_counter) begin // irq reset on load!
|
|
t2_irq <= 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
//
|
|
// Shift Register
|
|
//
|
|
|
|
reg sr_dir_out ;
|
|
reg sr_ena ;
|
|
reg sr_cb1_op ;
|
|
reg sr_cb1_ip ;
|
|
reg sr_use_t2 ;
|
|
reg sr_free_run ;
|
|
reg sr_count_ena ;
|
|
|
|
|
|
always @(posedge CLK, negedge RESET_L) begin
|
|
if (RESET_L == 1'b0) begin
|
|
r_sr <= 8'h00;
|
|
sr_drive_cb2 <= 1'b0;
|
|
sr_cb1_oe_l <= 1'b1;
|
|
sr_cb1_out <= 1'b0;
|
|
sr_do_shift <= 1'b0;
|
|
sr_strobe <= 1'b1;
|
|
sr_cnt <= 4'b0000;
|
|
sr_irq <= 1'b0;
|
|
sr_out <= 1'b0;
|
|
sr_active <= 1'b0;
|
|
end else begin
|
|
if (ENA_4 == 1'b1) begin
|
|
// decode mode
|
|
sr_dir_out = r_acr[4]; // output on cb2
|
|
sr_cb1_op = 1'b0;
|
|
sr_cb1_ip = 1'b0;
|
|
sr_use_t2 = 1'b0;
|
|
sr_free_run = 1'b0;
|
|
|
|
// DMB: SR still runs even in disabled mode (on rising edge of CB1).
|
|
// It just doesn't generate any interrupts.
|
|
// Ref BBC micro advanced user guide p409
|
|
|
|
case (r_acr[4:2])
|
|
// DMB: in disabled mode, configure cb1 as an input
|
|
3'b000: begin
|
|
sr_ena = 1'b0; sr_cb1_ip = 1'b1; // 0x00 Mode 0 SR disabled
|
|
end
|
|
3'b001: begin
|
|
sr_ena = 1'b1; sr_cb1_op = 1'b1; sr_use_t2 = 1'b1; // 0x04 Mode 1 Shift in under T2 control
|
|
end
|
|
3'b010: begin
|
|
sr_ena = 1'b1; sr_cb1_op = 1'b1; // 0x08 Mode 2 Shift in under P2 control
|
|
end
|
|
3'b011: begin
|
|
sr_ena = 1'b1; sr_cb1_ip = 1'b1; // 0x0C Mode 3 Shift in under control of ext clock
|
|
end
|
|
3'b100: begin
|
|
sr_ena = 1'b1; sr_cb1_op = 1'b1; sr_use_t2 = 1'b1; sr_free_run = 1'b1; // 0x10 Mode 4 Shift out free running under T2 control
|
|
end
|
|
3'b101: begin
|
|
sr_ena = 1'b1; sr_cb1_op = 1'b1; sr_use_t2 = 1'b1; // 0x14 Mode 5 Shift out under T2 control
|
|
end
|
|
3'b110: begin
|
|
sr_ena = 1'b1; sr_cb1_op = 1'b1; // 0x18 Mode 6 Shift out under P2 control
|
|
end
|
|
3'b111: begin
|
|
sr_ena = 1'b1; sr_cb1_ip = 1'b1; // 0x1C Mode 7 Shift out under control of ext clock
|
|
end
|
|
endcase
|
|
|
|
// clock select
|
|
// DMB: in disabled mode, strobe from cb1
|
|
if (sr_cb1_ip == 1'b1) begin
|
|
sr_strobe <= I_CB1;
|
|
end
|
|
else begin
|
|
if ((sr_cnt[3] == 1'b0) & (sr_free_run == 1'b0)) begin
|
|
sr_strobe <= 1'b1;
|
|
end
|
|
else begin
|
|
if (((sr_use_t2 == 1'b1) & t2_sr_ena) |
|
|
((sr_use_t2 == 1'b0) & (phase == 2'b00))) begin
|
|
sr_strobe <= !sr_strobe;
|
|
end
|
|
end
|
|
end
|
|
|
|
// latch on rising edge, shift on falling edge of P2
|
|
if (sr_write_ena) begin
|
|
r_sr <= load_data;
|
|
sr_out <= r_sr[7];
|
|
|
|
end
|
|
else begin
|
|
// DMB: allow shifting in all modes
|
|
if (sr_dir_out == 1'b0) begin
|
|
// input
|
|
if ((sr_cnt[3] == 1'b1) | (sr_cb1_ip == 1'b1)) begin
|
|
if (sr_strobe_rising) begin
|
|
sr_do_shift <= 1'b1;
|
|
r_sr[0] <= I_CB2;
|
|
end else if (sr_do_shift) begin
|
|
sr_do_shift <= 1'b0;
|
|
r_sr[7:1] <= r_sr[6:0];
|
|
end
|
|
end
|
|
end
|
|
else begin
|
|
// output
|
|
if ((sr_cnt[3] == 1'b1) | (sr_cb1_ip == 1'b1) | (sr_free_run == 1'b1)) begin
|
|
if (sr_strobe_falling) begin
|
|
sr_out <= r_sr[7];
|
|
sr_do_shift <= 1'b1;
|
|
end else if (sr_do_shift) begin
|
|
sr_do_shift <= 1'b0;
|
|
r_sr <= r_sr[6:0] & r_sr[7];
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
// Set shift enabled flag, note does not get set for free_run mode !
|
|
if ((sr_ena == 1'b1) & (sr_cnt[3] == 1'b1)) begin
|
|
sr_active <= 1'b1;
|
|
end else if ((sr_ena == 1'b1) & (sr_cnt[3] == 1'b0) & (phase == 2'b11)) begin
|
|
sr_active <= 1'b0;
|
|
end
|
|
|
|
sr_count_ena = sr_strobe_rising;
|
|
|
|
// DMB: reseting sr_count when not enabled cause the sr to
|
|
// start running immediately it was enabled, which is incorrect
|
|
// and broke the latest SmartSPI ROM on the BBC Micro
|
|
if ((sr_ena == 1'b1) & (sr_write_ena | sr_read_ena) & (!sr_active)) begin
|
|
// some documentation says sr bit in IFR must be set as well ?
|
|
sr_cnt <= 4'b1000;
|
|
end else if (sr_count_ena & (sr_cnt[3] == 1'b1)) begin
|
|
sr_cnt <= sr_cnt + 1'b1;
|
|
end
|
|
|
|
if (sr_count_ena & (sr_cnt == 4'b1111) & (sr_ena == 1'b1) & (sr_free_run == 1'b0)) begin
|
|
sr_irq <= 1'b1;
|
|
end else if (sr_write_ena | sr_read_ena | (clear_irq[2] == 1'b1)) begin
|
|
sr_irq <= 1'b0;
|
|
end
|
|
|
|
// assign ops
|
|
sr_drive_cb2 <= sr_dir_out;
|
|
sr_cb1_oe_l <= !sr_cb1_op;
|
|
sr_cb1_out <= sr_strobe;
|
|
end
|
|
end
|
|
end
|
|
|
|
always @(posedge CLK) begin
|
|
if (ENA_4 == 1'b1) begin
|
|
sr_strobe_t1 <= sr_strobe;
|
|
sr_strobe_rising <= (sr_strobe_t1 == 1'b0) & (sr_strobe == 1'b1);
|
|
sr_strobe_falling <= (sr_strobe_t1 == 1'b1) & (sr_strobe == 1'b0);
|
|
end
|
|
end
|
|
|
|
//
|
|
// Interrupts
|
|
//
|
|
|
|
always @(posedge CLK, negedge RESET_L) begin
|
|
if (RESET_L == 1'b0) begin
|
|
r_ier <= 7'b0000000;
|
|
end else begin
|
|
if (ENA_4 == 1'b1) begin
|
|
if (ier_write_ena) begin
|
|
if (load_data[7] == 1'b1) begin
|
|
// set
|
|
r_ier <= r_ier | load_data[6:0];
|
|
end
|
|
else begin
|
|
// clear
|
|
r_ier <= r_ier & !load_data[6:0];
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
assign O_IRQ_L = ~final_irq;
|
|
|
|
assign r_ifr = {final_irq, t1_irq, t2_irq, cb1_irq, cb2_irq, sr_irq, ca1_irq, ca2_irq};
|
|
|
|
|
|
always @(posedge CLK, negedge RESET_L) begin
|
|
if (RESET_L == 1'b0) begin
|
|
final_irq <= 1'b0;
|
|
end else begin
|
|
if (ENA_4 == 1'b1) begin
|
|
if ((r_ifr[6:0] & r_ier[6:0]) == 7'b0000000) begin
|
|
final_irq <= 1'b0; // no interrupts
|
|
end
|
|
else begin
|
|
final_irq <= 1'b1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
assign clear_irq = ifr_write_ena ? load_data : 8'h00;
|
|
|
|
endmodule
|