1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-01-18 09:02:08 +00:00
2019-07-23 21:42:55 +02:00

175 lines
5.1 KiB
Systemverilog

/* Atari on an FPGA
Masters of Engineering Project
Cornell University, 2007
Daniel Beer
RIOT.v
Redesign of the MOS 6532 chip. Provides RAM, I/O and timers to the Atari.
*/
`timescale 1ns / 1ps
`include "riot.vh"
module RIOT(A, // Address bus input
Din, // Data bus input
Dout, // Data bus output
CS, // Chip select input
CS_n, // Active low chip select input
R_W_n, // Active low read/write input
RS_n, // Active low rom select input
RES_n, // Active low reset input
IRQ_n, // Active low interrupt output
CLK, // Clock input
PAin, // 8 bit port A input
PAout, // 8 bit port A output
PBin, // 8 bit port B input
PBout);// 8 bit port B output
input [6:0] A;
input [7:0] Din;
output [7:0] Dout;
input CS, CS_n, R_W_n, RS_n, RES_n, CLK;
output IRQ_n;
input [7:0] PAin, PBin;
output [7:0] PAout, PBout; // Output register
reg [7:0] Dout; // RAM allocation
reg [7:0] RAM[127:0]; // I/O registers
reg [7:0] DRA, DRB; // Data registers
reg [7:0] DDRA, DDRB; // Data direction registers
wire PA7;
reg R_PA7;
assign PA7 = (PAin[7] & ~DDRA[7]) | (DRA[7] & DDRA[7]);
assign PAout = DRA & DDRA;
assign PBout = DRB & DDRB;
// Timer registers
reg [8:0] Timer;
reg [9:0] Prescaler;
reg [1:0] Timer_Mode;
reg Timer_Int_Flag, PA7_Int_Flag, Timer_Int_Enable, PA7_Int_Enable, PA7_Int_Mode; // Timer prescaler constants
wire [9:0] PRESCALER_VALS[3:0];
assign PRESCALER_VALS[0] = 10'd0;
assign PRESCALER_VALS[1] = 10'd7;
assign PRESCALER_VALS[2] = 10'd63;
assign PRESCALER_VALS[3] = 10'd1023;
// Interrupt
assign IRQ_n = ~(Timer_Int_Flag & Timer_Int_Enable | PA7_Int_Flag & PA7_Int_Enable);
// Operation decoding
wire [6:0] op;
reg [6:0] R_op;
assign op = {RS_n, R_W_n, A[4:0]};
// Registered data in
reg [7:0] R_Din;
integer cnt;
// Software operations
always @(posedge CLK)
begin
// Reset operation
if (~RES_n) begin
DRA <= 8'b0;
DDRA <= 8'b0;
DRB <= 8'b00010100;
DDRB <= 8'b00010100;
Timer_Int_Flag <= 1'b0;
PA7_Int_Flag <= 1'b0;
PA7_Int_Enable <= 1'b0;
PA7_Int_Mode <= 1'b0;
// Fill RAM with 0s
for (cnt = 0; cnt < 128; cnt = cnt + 1)
RAM[cnt] <= 8'b0;
R_PA7 <= 1'b0;
R_op <= `NOP;
R_Din <= 8'b0;
end
// If the chip is enabled, execute an operation
else if (CS & ~CS_n) begin
// Register inputs for use later
R_PA7 <= PA7;
R_op <= op;
R_Din <= Din;
// Update the timer interrupt flag
casex (op)
`WRITE_TIMER: Timer_Int_Flag <= 1'b0;
`READ_TIMER: Timer_Int_Flag <= 1'b0;
default: if (Timer == 9'b111111111) Timer_Int_Flag <= 1'b1;
endcase
// Update the port A interrupt flag
casex (op)
`READ_INT_FLAG: PA7_Int_Flag <= 1'b0;
default: PA7_Int_Flag <= PA7_Int_Flag | (PA7 != R_PA7 & PA7 == PA7_Int_Mode);
endcase
// Process the current operation
casex(op) // RAM access
`READ_RAM: Dout <= RAM[A];
`WRITE_RAM: RAM[A] <= Din;
// Port A data access
`READ_DRA : Dout <= (PAin & ~DDRA) | (DRA & DDRA);
`WRITE_DRA: DRA <= Din;
// Port A direction register access
`READ_DDRA: Dout <= DDRA;
`WRITE_DDRA: DDRA <= Din;
// Port B data access
`READ_DRB: Dout <= (PBin & ~DDRB) | (DRB & DDRB);
`WRITE_DRB: DRB <= Din;
// Port B direction register access
`READ_DDRB: Dout <= DDRB;
`WRITE_DDRB: DDRB <= Din;
// Timer access
`READ_TIMER: Dout <= Timer[7:0];
// Status register access
`READ_INT_FLAG: Dout <= {Timer_Int_Flag, PA7_Int_Flag, 6'b0};
// Enable the port A interrupt
`WRITE_EDGE_DETECT: begin
PA7_Int_Mode <= A[0]; PA7_Int_Enable <= A[1];
end
endcase
end
// Even if the chip is not enabled, update background functions
else begin
// Update the timer interrupt
if (Timer == 9'b111111111)
Timer_Int_Flag <= 1'b1;
// Update the port A interrupt
R_PA7 <= PA7;
PA7_Int_Flag <= PA7_Int_Flag | (PA7 != R_PA7 & PA7 == PA7_Int_Mode);
// Set the operation to a NOP
R_op <=`NOP;
end
end
// Update the timer at the negative edge of the clock
always @(negedge CLK)begin
// Reset operation
if (~RES_n) begin
Timer <= 9'b0;
Timer_Mode <= 2'b0;
Prescaler <= 10'b0;
Timer_Int_Enable <= 1'b0;
end
// Otherwise, process timer operations
else
casex
(R_op)
// Write value to the timer and update the prescaler based on the address
`WRITE_TIMER:begin
Timer <= {1'b0, R_Din};
Timer_Mode <= R_op[1:0];
Prescaler <= PRESCALER_VALS[R_op[1:0]];
Timer_Int_Enable <= R_op[3];
end
// Otherwise decrement the prescaler and if necessary the timer.
// The prescaler holds a variable number of counts that must be
// run before the timer is decremented
default:if (Timer != 9'b100000000) begin
if (Prescaler != 10'b0)
Prescaler <= Prescaler - 10'b1;
else begin
if (Timer == 9'b0)
begin
Prescaler <= 10'b0;
Timer_Mode <= 2'b0;
end
else
Prescaler <= PRESCALER_VALS[Timer_Mode];
Timer <= Timer - 9'b1;
end
end
endcase
end
endmodule