// UDISK CPLD // $Id: udisk_cpld.v 28 2006-08-30 14:56:55Z brad $ module udisk_cpld( BBSY_IN, MSYN_IN, BUS_ADDR, BUS_ADDR_DIR, BUS_DATA, BUS_DATA_DIR, PA_IN, PA_OUT, PB_IN, PB_OUT, INIT_IN, BG4_IN, BG5_IN, NPG_IN, SACK_IN, SSYN_IN, C0_IN, C1_IN, BG4_OUT, BG5_OUT, NPG_OUT, INTR_OUT, BR4_OUT, BR5_OUT, NPR_OUT, MSYN_OUT, SSYN_OUT, BBSY_OUT, C0_OUT, C1_OUT, SACK_OUT, CPU_D, CPU_INT, CPU_RD, CPU_WR, CPU_A0, CPU_A1, CPU_A2, CPU_A3, LED_OUT, CF_CS0_N, CF_CS1_N, CF_IORD_N, CF_IOWR_N, RESET_L, CLK, DISK_RESET_N); // Input input BBSY_IN, MSYN_IN, SSYN_IN, C0_IN, C1_IN; input INIT_IN, BG4_IN, BG5_IN, NPG_IN, SACK_IN; input CPU_RD, CPU_WR, CPU_A0, CPU_A1, CPU_A2, CPU_A3, RESET_L; input PA_IN, PB_IN; input CLK; // Output output BG4_OUT, BG5_OUT, NPG_OUT; output INTR_OUT, BR4_OUT, BR5_OUT, NPR_OUT; output MSYN_OUT, SSYN_OUT, BBSY_OUT, C0_OUT, C1_OUT, SACK_OUT; output CPU_INT; output BUS_ADDR_DIR, BUS_DATA_DIR; output LED_OUT; output CF_CS0_N, CF_CS1_N, CF_IORD_N, CF_IOWR_N; output PA_OUT, PB_OUT; output DISK_RESET_N; // Bidirectional inout [17:0] BUS_ADDR; inout [15:0] BUS_DATA; inout [15:0] CPU_D; // Registers reg [15:0] data_out; reg [17:2] addr_match_1; reg [9:2] addr_mask_1; reg [17:2] addr_match_2; reg [9:2] addr_mask_2; reg match_1, match_2; reg [17:0] addr_matching; reg [17:0] addr_out; reg [3:0] pass_thru; reg [15:0] assert; reg [15:0] unibus_data_out; reg CPU_INT; reg LED_OUT; reg [1:0] cf_enable; reg disk_reset; // bidir cpu data bus - assert if cpu reads and it's not an ide access assign CPU_D = (CPU_RD & CF_CS0_N & CF_CS1_N) ? data_out : 16'bz; // bidir unibus address bus assign BUS_ADDR = BUS_ADDR_DIR ? addr_out : 18'bz; // bidir unibus data bus assign BUS_DATA = BUS_DATA_DIR ? unibus_data_out : 16'bz; // signals asserted on unibus (via 8641, which inverts to _L signals) assign INTR_OUT = assert[0]; assign BR4_OUT = assert[1]; assign BR5_OUT = assert[2]; assign NPR_OUT = assert[3]; // unibus grant "pass through" enable assign BG4_OUT = pass_thru[1] ? BG4_IN : assert[4]; assign BG5_OUT = pass_thru[2] ? BG5_IN : assert[5]; assign NPG_OUT = pass_thru[3] ? NPG_IN : assert[6]; // signals used by cpu when unibus bus master assign MSYN_OUT = assert[8]; assign SSYN_OUT = assert[9]; assign BBSY_OUT = assert[10]; assign C0_OUT = assert[11]; assign C1_OUT = assert[12]; assign SACK_OUT = assert[13]; assign BUS_DATA_DIR = assert[14]; assign BUS_ADDR_DIR = assert[15]; // for now, these are dormant assign PA_OUT = 0; assign PB_OUT = 0; // match and interrupt logic wire cpu_int_reset, cpu_int_reset_write; wire have_match1, have_match2; wire [17:2] mask1, mask2; // decode write to cpu int reset register assign cpu_int_reset_write = ( {CPU_A3, CPU_A2, CPU_A1, CPU_A0} == 4'b0010 ) & (CPU_WR & CF_CS0_N & CF_CS1_N); assign cpu_int_reset = cpu_int_reset_write | ~RESET_L; // CF signals - only active if A3 == 0 assign CF_CS0_N = ~(cf_enable[0] & ~CPU_A3); assign CF_CS1_N = ~(cf_enable[1] & ~CPU_A3); // CF/IDE reset assign DISK_RESET_N = ~disk_reset; // enable cf rd/wr if either cf chip select enabled and reg 0-7 access //assign CF_IORD_N = ~(CPU_RD & (~CF_CS1_N | ~CF_CS0_N)); //assign CF_IOWR_N = ~(CPU_WR & (~CF_CS1_N | ~CF_CS0_N)); assign CF_IORD_N = ~(CPU_RD==1 & (CF_CS1_N==0 | CF_CS0_N==0)); assign CF_IOWR_N = ~(CPU_WR==1 & (CF_CS1_N==0 | CF_CS0_N==0)); // register read/write always @(RESET_L or CPU_A3 or CPU_A2 or CPU_A1 or CPU_A0 or CPU_RD or CPU_WR or CPU_D or assert or INIT_IN or BG4_IN or BG5_IN or NPG_IN or SACK_IN or CPU_INT or match_1 or match_2 or addr_matching or C1_IN or C0_IN or BUS_DATA or cf_enable or BBSY_IN) begin if (~RESET_L) begin pass_thru = 4'b1111; assert = 16'b0; LED_OUT = 0; addr_match_1 = 0; addr_mask_1 = 0; addr_match_2 = 0; addr_mask_2 = 0; end else if (CPU_WR) begin // ignore writes if ide enabled if (~cf_enable[0] & ~cf_enable[1]) // switch on A3,A2,A1,A0 casex ( {CPU_A3, CPU_A2, CPU_A1, CPU_A0} ) 4'b0000: assert = CPU_D; 4'b0001: unibus_data_out = CPU_D; // 4'b0010: CPU_INT = 0; 4'b0011: pass_thru = CPU_D[3:0]; 4'b0100: addr_out[17:16] = CPU_D[1:0]; 4'b0101: addr_out[15:0] = CPU_D; 4'b0110: LED_OUT = CPU_D[0]; 4'b1000: addr_match_1[17:2] = CPU_D; 4'b1001: addr_mask_1[9:2] = CPU_D[7:0]; 4'b1010: addr_match_2[17:2] = CPU_D; 4'b1011: addr_mask_2[9:2] = CPU_D[7:0]; // 4'b1100: begin // disk_reset = CPU_D[2]; // cf_enable = CPU_D[1:0]; // end endcase end else if (CPU_RD) begin // data_out is not enabled to CPU_D if ide enabled case ( {CPU_A3, CPU_A2, CPU_A1, CPU_A0} ) 4'b0000: data_out = assert; 4'b0001: data_out = BUS_DATA; 4'b0010: data_out[0] = CPU_INT; 4'b0011: data_out = { BBSY_IN, C1_IN, C0_IN, SACK_IN, BG4_IN, BG5_IN, NPG_IN, INIT_IN }; 4'b0100: data_out = { 6'b000000, match_2, match_1 }; 4'b1000: data_out = { 6'b000000, addr_matching[17:16] }; 4'b1001: data_out = addr_matching; // these 3 are just for debugging - they can be removed 4'b1100: data_out = { 6'b000000, have_match2, have_match1 }; 4'b1101: data_out = { 6'b000000, BUS_ADDR[17:16] }; 4'b1110: data_out = BUS_ADDR[15:0]; 4'b1111: data_out = 8'b10100101; endcase end end // special case resetting cf_enable - it always works always @(RESET_L or CPU_A3 or CPU_A2 or CPU_A1 or CPU_A0 or CPU_WR or CPU_D) begin if (~RESET_L) begin cf_enable = 2'b0; disk_reset = 0; end else if ( CPU_WR & {CPU_A3, CPU_A2, CPU_A1, CPU_A0} == 4'b1100) begin disk_reset = CPU_D[2]; cf_enable = CPU_D[1:0]; end end // watch for address matches on UNIBUS // only the bottom 10 bits are maskable assign mask1 = { 8'b11111111, addr_mask_1 }; assign mask2 = { 8'b11111111, addr_mask_2 }; assign have_match1 = ((BUS_ADDR[17:2] & mask1) == addr_match_1[17:2]) ? 1 : 0; assign have_match2 = ((BUS_ADDR[17:2] & mask2) == addr_match_2[17:2]) ? 1 : 0; // if BBSY_IN && MSYN_IN & Axx compare - generate cpu interrupt always @(posedge MSYN_IN or posedge cpu_int_reset) begin if (cpu_int_reset) begin CPU_INT = 0; match_1 = 0; match_2 = 0; end else if (BBSY_IN & (have_match1 | have_match2)) begin CPU_INT = 1; match_1 = have_match1; match_2 = have_match2; addr_matching = BUS_ADDR; end end endmodule