mirror of
https://github.com/Gehstock/Mist_FPGA.git
synced 2026-01-18 00:52:09 +00:00
175 lines
5.1 KiB
Systemverilog
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
|