mirror of
https://github.com/livingcomputermuseum/cpus-pdp8.git
synced 2026-01-13 15:37:04 +00:00
1126 lines
24 KiB
Verilog
1126 lines
24 KiB
Verilog
// RF08 Emulation using IDE disk
|
|
// brad@heeltoe.com
|
|
|
|
//`define debug_rf
|
|
|
|
/*
|
|
RF08 Sizes:
|
|
|
|
2048 words/track 11 bits
|
|
128 tracks 7 bits
|
|
4 disks 2 bits
|
|
-------
|
|
20 bits
|
|
|
|
1 111
|
|
dma 98765432109876543210
|
|
ddtttttttwwwwwwwwwww
|
|
ema 876543210
|
|
|
|
mapping RF08 to IDE disk drive
|
|
2048 x 12 bits -> 2048 x 16 bits = 8 blocks of 512 bytes
|
|
each track is 8 blocks
|
|
each disk is (128 * 8) = 1024 blocks
|
|
|
|
ide_block = (track * 8) + (word / 256)
|
|
ide_block_index = word % 256
|
|
|
|
ema bits 7 & 8 select which rs08 disk
|
|
ema bits 6 - 0 select disk head (track #)
|
|
|
|
dma contains lower disk word address (offset into block)
|
|
|
|
writes to dma trigger i/o; adc is asserted after match w/disk
|
|
|
|
-------------
|
|
|
|
PDP-8 memory:
|
|
|
|
7750 word count
|
|
7751 current address
|
|
|
|
PDP-8 IOT's:
|
|
|
|
660x
|
|
661x
|
|
662x
|
|
664x
|
|
|
|
6601 DCMA Generates System Clear Pulse (SCLP) at IOP time 1.
|
|
Clears disk memory eaddress(DMA), Parity Eror Flag (PEF),
|
|
Data Request Late Flag (DRL), and sets logic to
|
|
initial state for read or write. Does not clear
|
|
interrupt enable or extended address register.
|
|
|
|
6603 DMAR Generate SCLP at IOP time 1. At IOP time 2, loads DMA
|
|
with AC and clears AC. Read continues for number words
|
|
in WC register (7750)
|
|
|
|
6605 DMAW Generate SCLP at IOP time 1. At IOP time 4, loads DMA
|
|
with AC and clears AC. When the disk word address is located
|
|
writing begins, disk address is incremented for each
|
|
word written
|
|
|
|
6611 DCIM clears disk interrupt enable and the extended address
|
|
registers at IOP time 1.
|
|
|
|
6612 DSAC At IOP time 2, skip if Address Confirmed (ADC) set indicating
|
|
the DMA address and disk word address compare. AC is then
|
|
cleared.
|
|
|
|
6615 DIML At IOP time 1, clear interrupt enable and memory address
|
|
extension registers. At IOP time 4, load interrupt enable
|
|
and memory address extension register with AC, clear AC.
|
|
|
|
6616 DIMA Clear ??? at IOP time 2. At IOP time 4 load AC with status
|
|
register.
|
|
|
|
6621 DFSE skip on error skip if DRL, PER WLS or NXD set
|
|
|
|
6622 ??? skip if data completion DCF set
|
|
|
|
6623 DISK skip on error or data completion; enabled at IOP 2
|
|
|
|
6626 DMAC clear ac at IOP time 2 load AC from DMA at IOP time 4.
|
|
|
|
6641 DCXA Clears EMA
|
|
|
|
6643 DXAL Clears and loads EMA from AC. At IOP time 1, clear EMA, at
|
|
IOP time 2, load EMA with AC. Clear AC
|
|
|
|
6645 DXAC Clears AC and loads EMA into AC
|
|
|
|
6646 DMMT Maintenance
|
|
|
|
Real RF08 uses 3 cycle data break
|
|
|
|
ac 8:0, ac 10:0 => 20 bit {EMA,DMA}
|
|
20 bit {EMA,DMA} = { disk-select, track-select 6:0, word-select 11:0 }
|
|
|
|
status
|
|
EIE = WLS | DRL | NXD | PER
|
|
|
|
*/
|
|
|
|
|
|
/*
|
|
3 cycle data break
|
|
|
|
1. An address is read from the device to indicate the location of the
|
|
word count register. This location specifies the number of words in
|
|
the block yet to be transferred. The address is always the same for a
|
|
given device.
|
|
|
|
2. The content of the specficified word count register is read from
|
|
memory and incremented by one. To transfer a block of n words, the
|
|
word count is set to -n during the programmed initialization of the
|
|
device. When this register is incremented to 0, a pulse is sent to
|
|
the device to terminate the transfer.
|
|
|
|
3. The location after the word count register contains the current
|
|
address register for the device transfer. The content of this
|
|
register is set to 1 less than the location to be affected by the next
|
|
transfer. To transfer a block beginning at location A, the register is
|
|
originally set to A-1.
|
|
|
|
4. The content of the current address register is incremented by 1
|
|
and then used to specify the location affected by the transfer.
|
|
|
|
After the transfer of information has been accomplished through the
|
|
data break factility, input data (or new output data) is processed,
|
|
usually through the program interrupt facility. An interrupt is
|
|
requested when the data transfer is completed and the service routine
|
|
will process the information.
|
|
|
|
----------- ----------- ----------- -----------
|
|
*/
|
|
|
|
/*
|
|
|
|
HIGH LEVEL DISK STATE MACHINE:
|
|
|
|
idle
|
|
|
|
start-xfer
|
|
read wc
|
|
read addr
|
|
|
|
if read
|
|
goto check-xfer-read
|
|
else
|
|
goto begin-xfer-write
|
|
|
|
check-xfer-read
|
|
if memory buffer contains disk page
|
|
read word from memory buffer at offset
|
|
goto next-xfer-read
|
|
else
|
|
if memory buffer dirty
|
|
goto write-old-page
|
|
else
|
|
goto read-new-page
|
|
|
|
next-xfer-read
|
|
write memory at addr
|
|
goto next-xfer-incr
|
|
|
|
next-xfer-incr
|
|
incr addr
|
|
incr wc
|
|
if wc == 0
|
|
goto done-xfer
|
|
if read
|
|
goto check-xfer-read
|
|
else
|
|
goto begin-xfer-write
|
|
|
|
begin-xfer-write
|
|
read from memory at addr
|
|
goto check-xfer-write
|
|
|
|
check-xfer-write
|
|
if memory buffer contains disk page
|
|
write word to memory buffer at offset
|
|
set memory buffer dirty
|
|
goto next-xfer-incr
|
|
else
|
|
if memory buffer dirty
|
|
goto write-old-page
|
|
else
|
|
goto read-new-page
|
|
|
|
done-xfer
|
|
write addr to memory
|
|
write wc to memory
|
|
set done/interrupt
|
|
goto idle
|
|
|
|
read-new-page
|
|
read memory buffer from ide
|
|
remember memory buffer disk address
|
|
clear memory buffer dirty
|
|
if read
|
|
goto check-xfer-read
|
|
else
|
|
goto check-xfer-write
|
|
|
|
write-old-page
|
|
write memory buffer to ide
|
|
clear memory buffer dirty
|
|
goto read-new-page
|
|
|
|
----------- ----------- ----------- -----------
|
|
|
|
DISK / DMA STATE MACHINE:
|
|
|
|
external signals:
|
|
|
|
ma_out
|
|
ram_write_req
|
|
ram_read_req
|
|
ram_done
|
|
mb_in
|
|
mb_out
|
|
|
|
internal signals:
|
|
|
|
reg [19:0] disk_addr;
|
|
|
|
wire [8:0] track;
|
|
wire [11:0] ide_block;
|
|
wire [7:0] ide_block_index;
|
|
|
|
track = {ema[7:0], dma[11]};
|
|
ide_block = {1'b0, track, 3'b0} + {8'b0, dma[11:8]}
|
|
ide_block_index = dma[7:0];
|
|
|
|
|
|
db_done <= 0;
|
|
dma_done = 0;
|
|
ram_read_req = 0;
|
|
ram_write_req = 0;
|
|
|
|
// idle
|
|
DB_idle:
|
|
|
|
// read word count
|
|
DB_start_xfer1:
|
|
disk_addr <= {ema, dma};
|
|
ma_out = wc-address
|
|
ram_read_req = 1
|
|
dma_wc <= mb_in + 1;
|
|
if ram_done db_next_state = DB_start_xfer2;
|
|
|
|
// read addr
|
|
DB_start_xfer2:
|
|
ma_out = wc-address | 1
|
|
ram_read_req = 1
|
|
dma_addr <= mb_in + 1
|
|
db_next_state = DB_start_xfer3;
|
|
|
|
DB_start_xfer3:
|
|
dma_done = dma_wc == 0
|
|
if read
|
|
db_next_state = DB_check_xfer_read;
|
|
else
|
|
db_next_state = DB_begin_xfer_write;
|
|
|
|
// check buffer address
|
|
DB_check_xfer_read:
|
|
buffer_addr = disk_addr_offset
|
|
if disk-addr-page == memory-buffer-addr-page
|
|
buffer_rd = 1
|
|
db_next_state = DB_next_xfer_read;
|
|
else
|
|
if buffer_dirty == 0
|
|
db_next_state = DB_read_new_page;
|
|
else
|
|
db_next_state = DB_write_old_page;
|
|
|
|
// write to ram
|
|
DB_next_xfer_read:
|
|
disk_addr <= disk_addr + 1
|
|
ma_out = dma_addr
|
|
mb_out = buffer_out
|
|
ram_write_req = 1
|
|
|
|
if ma_out = 7750
|
|
dma_wc = buffer_out + 1
|
|
|
|
if ma_out = 7751
|
|
dma_addr = buffer_out + 1
|
|
|
|
if ram_done
|
|
else
|
|
dma_done = dma_wc == 0
|
|
if dma_done
|
|
db_next_state = DB_done_xfer
|
|
else
|
|
db_next_state = DB_next_xfer_incr;
|
|
|
|
DB_next_xfer_incr:
|
|
dma_addr <= dma_addr + 1
|
|
dma_wc <= dma_wc + 1
|
|
if read
|
|
db_next_state = DB_check_xfer_read;
|
|
else
|
|
db_next_state = DB_begin_xfer_write;
|
|
|
|
// read from ram
|
|
DB_begin_xfer_write:
|
|
ma_out = dma_addr
|
|
buffer_hold <= ram_in
|
|
ram_read_req = 1
|
|
if ram_done db_next_state = DB_check_xfer_write;
|
|
|
|
// check buffer address
|
|
DB_check_xfer_write:
|
|
buffer_addr = disk_addr_offset
|
|
if disk-addr-page == memory-buffer-addr-page
|
|
buffer_wr = 1
|
|
buffer_dirty <= 1
|
|
disk_addr <= disk_addr + 1
|
|
dma_done = dma_wc == 0
|
|
if dma_done
|
|
db_next_state = DB_done_xfer
|
|
else
|
|
db_next_state = DB_next_xfer_incr;
|
|
else
|
|
if buffer_dirty == 0
|
|
db_next_state = DB_read_new_page
|
|
else
|
|
db_next_state = DB_write_old_page
|
|
|
|
// done
|
|
DB_done_xfer:
|
|
ema <= disk_addr[18:11];
|
|
dma <= disk_addr[10:0];
|
|
|
|
ma_out = wc-address;
|
|
mb_out = dma_addr
|
|
ram_write_req = 1;
|
|
if ram_done db_next_state = DB_start_xfer1;
|
|
|
|
DB_done_xfer1:
|
|
ma_out = wc-address | 1;
|
|
mb_out = dma_wc
|
|
ram_write_req = 1;
|
|
if ram_done db_next_state = DB_done_xfer2;
|
|
|
|
DB_done_xfer2:
|
|
// wait for F2
|
|
if state == F2
|
|
db_next_state = DB_done_xfer3
|
|
else
|
|
db_next_state = DB_done_xfer2
|
|
|
|
DB_done_xfer3:
|
|
db_done <= 1
|
|
//interrupt
|
|
db_next_state = DB_idle
|
|
|
|
DB_read_new_page:
|
|
read block from ide
|
|
set memory-buffer-addr-page
|
|
buffer_dirty <= 0
|
|
if read
|
|
db_next_state = DB_check_xfer_read
|
|
else
|
|
db_next_state = DB_check_xfer_write
|
|
|
|
DB_write_old_page:
|
|
write block to ide
|
|
buffer_dirty <= 0
|
|
db_next_state = DB_read_new_page
|
|
|
|
----------- ----------- ----------- -----------
|
|
*/
|
|
|
|
|
|
module pdp8_rf(clk, reset, iot, state, mb,
|
|
io_data_in, io_data_out, io_select, io_selected,
|
|
io_data_avail, io_interrupt, io_skip, io_clear_ac,
|
|
ram_read_req, ram_write_req, ram_done,
|
|
ram_ma, ram_in, ram_out,
|
|
ide_dior, ide_diow, ide_cs, ide_da, ide_data_bus);
|
|
|
|
input clk, reset, iot;
|
|
input [11:0] io_data_in;
|
|
input [11:0] mb;
|
|
input [3:0] state;
|
|
input [5:0] io_select;
|
|
input ram_done;
|
|
input [11:0] ram_in;
|
|
|
|
output reg [11:0] io_data_out;
|
|
output reg io_selected;
|
|
output reg io_data_avail;
|
|
output io_interrupt;
|
|
output reg io_skip;
|
|
output reg io_clear_ac;
|
|
|
|
output ram_read_req;
|
|
output ram_write_req;
|
|
output [11:0] ram_out;
|
|
output [14:0] ram_ma;
|
|
|
|
output ide_dior;
|
|
output ide_diow;
|
|
output [1:0] ide_cs;
|
|
output [2:0] ide_da;
|
|
inout [15:0] ide_data_bus;
|
|
|
|
// -------------------------------------------------------
|
|
|
|
parameter [3:0]
|
|
F0 = 4'b0000,
|
|
F1 = 4'b0001,
|
|
F2 = 4'b0010,
|
|
F3 = 4'b0011;
|
|
|
|
parameter PCA_bit = 12'o4000; // photocell status
|
|
parameter DRE_bit = 12'o2000; // data req enable
|
|
parameter WLS_bit = 12'o1000; // write lock status
|
|
parameter EIE_bit = 12'o0400; // error int enable
|
|
parameter PIE_bit = 12'o0200; // photocell int enb
|
|
parameter CIE_bit = 12'o0100; // done int enable
|
|
parameter MEX_bit = 12'o0070; // memory extension
|
|
parameter DRL_bit = 12'o0004; // data late error
|
|
parameter NXD_bit = 12'o0002; // non-existent disk
|
|
parameter PER_bit = 12'o0001; // parity error
|
|
|
|
parameter WC_ADDR = 15'o07750;
|
|
parameter CA_ADDR = 15'o07751;
|
|
|
|
wire ADC;
|
|
wire DRE;
|
|
wire DRL;
|
|
wire PER;
|
|
wire PCA;
|
|
|
|
reg [11:0] DMA;
|
|
reg [7:0] EMA;
|
|
reg DCF;
|
|
reg PEF;
|
|
reg CIE, EIE, NXD, PIE, WLS;
|
|
reg [2:0] MEX;
|
|
|
|
assign DRL = 1'b0;
|
|
assign PER = 1'b0;
|
|
|
|
reg [7:0] photocell_counter;
|
|
|
|
parameter [3:0]
|
|
DB_idle = 4'd0,
|
|
DB_start_xfer1 = 4'd1,
|
|
DB_start_xfer2 = 4'd2,
|
|
DB_start_xfer3 = 4'd3,
|
|
DB_check_xfer_read = 4'd4,
|
|
DB_next_xfer_read = 4'd5,
|
|
DB_next_xfer_incr = 4'd6,
|
|
DB_begin_xfer_write = 4'd7,
|
|
DB_check_xfer_write = 4'd8,
|
|
DB_done_xfer = 4'd9,
|
|
DB_done_xfer1 = 4'd10,
|
|
DB_done_xfer2 = 4'd11,
|
|
DB_done_xfer3 = 4'd12,
|
|
DB_read_new_page = 4'd13,
|
|
DB_write_old_page = 4'd14;
|
|
|
|
reg [3:0] db_next_state;
|
|
reg [3:0] db_state;
|
|
|
|
wire active;
|
|
reg is_read;
|
|
reg is_write;
|
|
|
|
reg dma_start;
|
|
reg db_done;
|
|
wire dma_done;
|
|
|
|
reg clear_db_done;
|
|
reg set_db_done;
|
|
|
|
reg [14:0] dma_addr;
|
|
reg [11:0] dma_wc;
|
|
|
|
reg [19:0] disk_addr;
|
|
|
|
reg load_disk_addr;
|
|
reg incr_disk_addr;
|
|
|
|
wire [7:0] buffer_addr;
|
|
reg [19:8] buffer_disk_addr;
|
|
reg buffer_dirty;
|
|
|
|
reg [11:0] buffer_hold;
|
|
|
|
reg load_buffer_hold;
|
|
reg set_buffer_addr;
|
|
reg set_buffer_dirty;
|
|
|
|
wire buffer_matches_DMA;
|
|
wire buffer_rd;
|
|
wire buffer_wr;
|
|
|
|
wire ide_read_req;
|
|
wire ide_write_req;
|
|
wire ide_done;
|
|
wire ide_error;
|
|
|
|
//
|
|
assign io_interrupt = (CIE & DCF) ||
|
|
(PIE & PCA) ||
|
|
(EIE & (WLS | DRL | NXD | PER));
|
|
|
|
assign active = is_read | is_write;
|
|
|
|
assign buffer_matches_DMA = buffer_disk_addr[19:8] == disk_addr[19:8];
|
|
assign buffer_addr = disk_addr[7:0];
|
|
|
|
//
|
|
// sector buffer
|
|
//
|
|
wire ide_active;
|
|
wire [7:0] buff_addr;
|
|
wire [11:0] buff_in;
|
|
wire [11:0] buff_out;
|
|
wire buff_rd;
|
|
wire buff_wr;
|
|
|
|
wire [7:0] ide_buffer_addr;
|
|
wire [23:0] ide_block_number;
|
|
wire [11:0] ide_buffer_in;
|
|
wire [11:0] ide_buffer_out;
|
|
wire ide_buffer_rd;
|
|
wire ide_buffer_wr;
|
|
|
|
// ide sector buffer
|
|
ram_256x12 buffer(.clk(clk),
|
|
.reset(reset),
|
|
.a(buff_addr),
|
|
.din(buff_in),
|
|
.dout(buff_out),
|
|
.ce(buff_rd | buff_wr),
|
|
.we(buff_wr));
|
|
|
|
assign ide_buffer_in = buff_out;
|
|
|
|
assign ide_active = ide_read_req | ide_write_req;
|
|
|
|
assign buff_addr = ide_active ? ide_buffer_addr : buffer_addr;
|
|
assign buff_in = ide_active ? ide_buffer_out : buffer_hold;
|
|
assign buff_rd = ide_active ? ide_buffer_rd : 1'b1/*buffer_rd?*/;
|
|
assign buff_wr = ide_active ? ide_buffer_wr : buffer_wr;
|
|
|
|
// ide disk
|
|
ide_disk disk(.clk(clk),
|
|
.reset(reset),
|
|
.ide_lba(ide_block_number),
|
|
.ide_read_req(ide_read_req),
|
|
.ide_write_req(ide_write_req),
|
|
.ide_error(ide_error),
|
|
.ide_done(ide_done),
|
|
.buffer_addr(ide_buffer_addr),
|
|
.buffer_rd(ide_buffer_rd),
|
|
.buffer_wr(ide_buffer_wr),
|
|
.buffer_in(ide_buffer_in),
|
|
.buffer_out(ide_buffer_out),
|
|
.ide_data_bus(ide_data_bus),
|
|
.ide_dior(ide_dior),
|
|
.ide_diow(ide_diow),
|
|
.ide_cs(ide_cs),
|
|
.ide_da(ide_da));
|
|
|
|
assign ide_block_number = { 12'b0, disk_addr[19:8] };
|
|
|
|
//
|
|
// RF controller
|
|
//
|
|
|
|
// combinatorial logic
|
|
always @(state or iot or io_select or mb or io_data_in or
|
|
ADC or DRL or PER or WLS or NXD or DCF or
|
|
PCA or DRE or EIE or PIE or CIE or MEX or DMA or EMA or
|
|
disk_addr)
|
|
begin
|
|
// sampled during f1
|
|
io_skip = 0;
|
|
io_clear_ac = 0;
|
|
io_data_out = io_data_in;
|
|
io_data_avail = 1'b1;
|
|
dma_start = 1'b0;
|
|
io_selected = 1'b0;
|
|
|
|
if (state == F1 && iot)
|
|
case (io_select)
|
|
6'o60:
|
|
begin
|
|
io_selected = 1'b1;
|
|
case (mb[2:0] )
|
|
3'o3: // DMAR
|
|
begin
|
|
io_data_out = 0;
|
|
dma_start = 1'b1;
|
|
io_clear_ac = 1;
|
|
//`ifdef debug
|
|
// $display("rf: go! disk_addr %o", disk_addr);
|
|
//`endif
|
|
end
|
|
3'o5: // DMAW
|
|
begin
|
|
io_data_out = 0;
|
|
dma_start = 1'b1;
|
|
io_clear_ac = 1;
|
|
end
|
|
endcase
|
|
end // case: 6'o60
|
|
|
|
6'o61:
|
|
begin
|
|
io_selected = 1'b1;
|
|
case (mb[2:0])
|
|
3'o2: // DSAC
|
|
if (ADC)
|
|
begin
|
|
io_skip = 1;
|
|
io_data_out = 0;
|
|
end
|
|
3'o6: // DIMA
|
|
io_data_out = { PCA, DRE,WLS,
|
|
EIE, PIE,CIE,
|
|
MEX,
|
|
DRL,NXD,PER };
|
|
3'o5: // DIML
|
|
begin
|
|
io_data_out = 0;
|
|
io_clear_ac = 1;
|
|
end
|
|
endcase // case(mb[2:0])
|
|
end
|
|
|
|
6'o62:
|
|
begin
|
|
io_selected = 1'b1;
|
|
case (mb[2:0])
|
|
3'o1: // DFSE
|
|
if (DRL | PER | WLS | NXD)
|
|
io_skip = 1;
|
|
3'o2: // ???
|
|
if (DCF)
|
|
io_skip = 1;
|
|
3'o3: // DISK
|
|
if (DRL | PER | WLS | NXD | DCF)
|
|
io_skip = 1;
|
|
3'o6: // DMAC
|
|
begin
|
|
io_data_out = DMA;
|
|
io_clear_ac = 1;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
6'o64:
|
|
begin
|
|
io_selected = 1'b1;
|
|
case (mb[2:0])
|
|
3'o3: // DXAL
|
|
begin
|
|
io_data_out = 0;
|
|
io_clear_ac = 1;
|
|
end
|
|
3'o5: // DXAC
|
|
io_data_out = EMA;
|
|
endcase // case(mb[2:0])
|
|
end
|
|
|
|
endcase // case(io_select)
|
|
end
|
|
|
|
|
|
//
|
|
// registers
|
|
//
|
|
always @(posedge clk)
|
|
if (reset)
|
|
begin
|
|
is_read <= 1'b0;
|
|
is_write <= 1'b0;
|
|
|
|
EMA <= 1'b0;
|
|
DMA <= 1'b0;
|
|
MEX <= 3'b0;
|
|
PEF <= 1'b0;
|
|
CIE <= 1'b0;
|
|
DCF <= 1'b1;
|
|
NXD <= 1'b0;
|
|
end
|
|
else
|
|
case (state)
|
|
F0:
|
|
begin
|
|
if (iot)
|
|
case (io_select)
|
|
6'o60: // DCMA
|
|
if (mb[2:0] == 3'b001)
|
|
begin
|
|
`ifdef debug
|
|
$display("rf: DCMA");
|
|
`endif
|
|
DMA <= 0;
|
|
PEF <= 1'b0;
|
|
NXD <= 1'b0;
|
|
DCF <= 1'b0;
|
|
end
|
|
6'o61:
|
|
case (mb[2:0])
|
|
3'o1: // DCIM
|
|
begin
|
|
EIE <= 1'b0;
|
|
PIE <= 1'b0;
|
|
CIE <= 1'b0;
|
|
MEX <= 3'b0;
|
|
`ifdef debug
|
|
$display("rf: DCIM");
|
|
`endif
|
|
end
|
|
3'o2: // DSAC
|
|
begin
|
|
end
|
|
3'o5: // DIML
|
|
begin
|
|
end
|
|
endcase // case(mb[2:0])
|
|
endcase
|
|
end
|
|
|
|
F1:
|
|
if (iot)
|
|
begin
|
|
`ifdef debug_rf
|
|
if (io_select == 6'o60 || io_select == 6'o64)
|
|
$display("iot2 %t, state %b, mb %o, io_select %o",
|
|
$time, state, mb, io_select);
|
|
`endif
|
|
case (io_select)
|
|
6'o60:
|
|
case (mb[2:0])
|
|
3'o3: // DMAR
|
|
begin
|
|
// clear ac
|
|
DMA <= io_data_in;
|
|
is_read <= 1'b1;
|
|
DCF <= 1'b0;
|
|
`ifdef debug
|
|
$display("rf: DMAR ac %o", io_data_in);
|
|
`endif
|
|
end
|
|
|
|
3'o5: // DMAW
|
|
begin
|
|
// clear ac
|
|
DMA <= io_data_in;
|
|
// is_write <= 1'b1;
|
|
//debug
|
|
is_read <= 1'b1;
|
|
DCF <= 1'b0;
|
|
end
|
|
endcase // case(mb[2:0])
|
|
|
|
6'o61:
|
|
case (mb[2:0])
|
|
3'o5: // DIML
|
|
begin
|
|
EIE <= io_data_in[8];
|
|
PIE <= io_data_in[7];
|
|
CIE <= io_data_in[6];
|
|
MEX <= io_data_in[5:3];
|
|
`ifdef debug
|
|
$display("rf: DIML %o", io_data_in);
|
|
`endif
|
|
end
|
|
endcase // case(mb[2:0])
|
|
|
|
6'o64:
|
|
case (mb[2:0])
|
|
1: // DCXA
|
|
EMA <= 0;
|
|
3: // DXAL
|
|
begin
|
|
// clear ac
|
|
EMA <= io_data_in;
|
|
end
|
|
endcase
|
|
|
|
endcase
|
|
|
|
end // if (iot)
|
|
|
|
F2:
|
|
begin
|
|
end
|
|
|
|
// F3 is a convenient time to do this
|
|
// note that state machine waits when done till next F2
|
|
// to sync up DB_done_xfer3 and F3
|
|
F3:
|
|
if (db_state == DB_done_xfer3)
|
|
begin
|
|
EMA <= disk_addr[19:12];
|
|
DMA <= disk_addr[11:0];
|
|
is_read <= 1'b0;
|
|
is_write <= 1'b0;
|
|
`ifdef debug
|
|
$display("rf: set DCF (CIE %b)", CIE);
|
|
`endif
|
|
DCF <= 1'b1;
|
|
end
|
|
|
|
endcase // case(state)
|
|
|
|
// comb logic to create 'next state'
|
|
always @(*)
|
|
begin
|
|
db_next_state = db_state;
|
|
load_disk_addr = 0;
|
|
incr_disk_addr = 0;
|
|
set_buffer_addr = 0;
|
|
set_buffer_dirty = 0;
|
|
load_buffer_hold = 0;
|
|
|
|
case (db_state)
|
|
DB_idle:
|
|
if (dma_start)
|
|
db_next_state = DB_start_xfer1;
|
|
|
|
DB_start_xfer1:
|
|
if (ram_done)
|
|
begin
|
|
clear_db_done = 1;
|
|
load_disk_addr = 1;
|
|
db_next_state = DB_start_xfer2;
|
|
end
|
|
|
|
DB_start_xfer2:
|
|
if (ram_done)
|
|
begin
|
|
set_db_done = 1;
|
|
db_next_state = DB_start_xfer3;
|
|
end
|
|
|
|
DB_start_xfer3:
|
|
db_next_state = is_read ? DB_check_xfer_read : DB_begin_xfer_write;
|
|
|
|
DB_check_xfer_read:
|
|
begin
|
|
if (buffer_matches_DMA)
|
|
db_next_state = DB_next_xfer_read;
|
|
else
|
|
db_next_state = buffer_dirty ?
|
|
DB_write_old_page :
|
|
DB_read_new_page;
|
|
end
|
|
|
|
DB_next_xfer_read:
|
|
if (ram_done)
|
|
begin
|
|
incr_disk_addr = 1;
|
|
db_next_state = dma_done ? DB_done_xfer : DB_next_xfer_incr;
|
|
end
|
|
|
|
DB_next_xfer_incr:
|
|
db_next_state = is_read ? DB_check_xfer_read:DB_begin_xfer_write;
|
|
|
|
DB_begin_xfer_write:
|
|
if (ram_done)
|
|
begin
|
|
load_buffer_hold = 1;
|
|
db_next_state = DB_check_xfer_write;
|
|
end
|
|
|
|
DB_check_xfer_write:
|
|
if (buffer_matches_DMA)
|
|
begin
|
|
set_buffer_dirty = 1;
|
|
incr_disk_addr = 1;
|
|
db_next_state = dma_done ? DB_done_xfer : DB_next_xfer_incr;
|
|
end
|
|
else
|
|
db_next_state = buffer_dirty ?
|
|
DB_write_old_page :
|
|
DB_read_new_page;
|
|
|
|
DB_done_xfer:
|
|
if (ram_done)
|
|
db_next_state = DB_done_xfer1;
|
|
|
|
DB_done_xfer1:
|
|
if (ram_done)
|
|
db_next_state = DB_done_xfer2;
|
|
|
|
DB_done_xfer2:
|
|
if (state == F2)
|
|
db_next_state = DB_done_xfer3;
|
|
|
|
DB_done_xfer3:
|
|
db_next_state = DB_idle;
|
|
|
|
DB_read_new_page:
|
|
if (ide_done)
|
|
begin
|
|
set_buffer_addr = 1;
|
|
db_next_state = is_read ?
|
|
DB_check_xfer_read :
|
|
DB_check_xfer_write;
|
|
end
|
|
|
|
DB_write_old_page:
|
|
if (ide_done)
|
|
begin
|
|
set_buffer_addr = 1;
|
|
db_next_state = DB_read_new_page;
|
|
end
|
|
|
|
endcase
|
|
end
|
|
|
|
// db_state
|
|
always @(posedge clk)
|
|
if (reset)
|
|
db_state <= DB_idle;
|
|
else
|
|
db_state <= db_next_state;
|
|
|
|
assign dma_done = dma_wc == 12'o0000;
|
|
|
|
// general state - wc & ca
|
|
always @(posedge clk)
|
|
if (reset)
|
|
begin
|
|
dma_wc <= 12'b0;
|
|
dma_addr <= 14'b0;
|
|
end
|
|
else
|
|
begin
|
|
case (db_state)
|
|
|
|
DB_start_xfer1:
|
|
begin
|
|
dma_wc <= ram_in + 12'o0001;
|
|
`ifdef debug
|
|
if (ram_done) $display("rf: read wc %o", ram_in);
|
|
`endif
|
|
end
|
|
|
|
DB_start_xfer2:
|
|
begin
|
|
dma_addr <= { MEX, ram_in + 12'o0001 };
|
|
`ifdef debug
|
|
|
|
if (ram_done) $display("rf: read ca %o", ram_in);
|
|
`endif
|
|
end
|
|
|
|
DB_start_xfer3:
|
|
begin
|
|
// this state might be not be needed
|
|
`ifdef debug
|
|
$display("rf: start! disk_addr %o (%o %o)",
|
|
disk_addr, EMA, DMA);
|
|
`endif
|
|
end
|
|
|
|
DB_next_xfer_incr:
|
|
begin
|
|
dma_addr[11:0] <= dma_addr[11:0] + 12'o00001;
|
|
dma_wc <= dma_wc + 12'b1;
|
|
`ifdef debug_rf
|
|
$display("dma_wc %o dma_addr %o MEX %o",dma_wc,dma_addr,MEX);
|
|
`endif
|
|
end
|
|
|
|
DB_next_xfer_read:
|
|
begin
|
|
/* snoop for our wc & ca */
|
|
if (dma_addr == 12'o7750 && ram_done)
|
|
begin
|
|
dma_wc <= buff_out;
|
|
$display("rf: snoop update wc %o", buff_out);
|
|
end
|
|
|
|
if (dma_addr == 12'o7751 && ram_done)
|
|
begin
|
|
dma_addr[11:0] <= buff_out;
|
|
$display("rf: snoop update ca %o", buff_out);
|
|
end
|
|
|
|
`ifdef debug_rf
|
|
if (ram_done)
|
|
$display("rf: buffer read[%o] = %o", buff_addr, buff_out);
|
|
`endif
|
|
end
|
|
|
|
`ifdef debug
|
|
DB_done_xfer:
|
|
if (ram_done) $display("rf: write wc %o", dma_wc);
|
|
|
|
DB_done_xfer1:
|
|
if (ram_done) $display("rf: write ca %o", dma_addr);
|
|
|
|
DB_done_xfer2:
|
|
$display("rf: done");
|
|
`endif
|
|
|
|
endcase
|
|
end
|
|
|
|
//
|
|
always @(posedge clk)
|
|
if (reset)
|
|
buffer_hold <= 12'b0;
|
|
else
|
|
if (load_buffer_hold)
|
|
buffer_hold <= ram_in;
|
|
|
|
// done state
|
|
always @(posedge clk)
|
|
if (reset)
|
|
db_done <= 1'b1;
|
|
else
|
|
if (clear_db_done)
|
|
db_done <= 1'b0;
|
|
else
|
|
if (set_db_done)
|
|
db_done <= 1'b1;
|
|
|
|
// buffer address
|
|
always @(posedge clk)
|
|
if (reset)
|
|
begin
|
|
buffer_disk_addr[19:8] <= 12'b111111111111;
|
|
buffer_dirty <= 1'b0;
|
|
end
|
|
else
|
|
if (set_buffer_dirty)
|
|
buffer_dirty <= 1'b1;
|
|
else
|
|
if (set_buffer_addr)
|
|
begin
|
|
buffer_dirty <= 1'b0;
|
|
buffer_disk_addr[19:8] <= disk_addr[19:8];
|
|
end
|
|
|
|
// disk address
|
|
always @(posedge clk)
|
|
if (reset)
|
|
disk_addr <= 0;
|
|
else
|
|
if (load_disk_addr)
|
|
disk_addr <= {EMA, DMA};
|
|
else
|
|
if (incr_disk_addr)
|
|
disk_addr <= disk_addr + 20'b1;
|
|
|
|
|
|
//
|
|
// external ram control (for dma to/from pdp-8 memory)
|
|
//
|
|
assign ram_ma =
|
|
db_state == DB_start_xfer1 ? WC_ADDR :
|
|
db_state == DB_start_xfer2 ? CA_ADDR :
|
|
db_state == DB_next_xfer_read ? dma_addr :
|
|
db_state == DB_begin_xfer_write ? dma_addr :
|
|
db_state == DB_done_xfer ? WC_ADDR :
|
|
db_state == DB_done_xfer1 ? CA_ADDR :
|
|
15'b0;
|
|
|
|
assign ram_read_req =
|
|
(db_state == DB_start_xfer1) |
|
|
(db_state == DB_start_xfer2) |
|
|
(db_state == DB_begin_xfer_write);
|
|
|
|
assign ram_write_req =
|
|
(db_state == DB_next_xfer_read) |
|
|
(db_state == DB_done_xfer) |
|
|
(db_state == DB_done_xfer1);
|
|
|
|
assign ram_out =
|
|
db_state == DB_next_xfer_read ? buff_out :
|
|
db_state == DB_begin_xfer_write ? buffer_hold :
|
|
db_state == DB_done_xfer ? dma_wc :
|
|
db_state == DB_done_xfer1 ? dma_addr :
|
|
12'b0;
|
|
|
|
assign buffer_rd = db_state == DB_check_xfer_read && buffer_matches_DMA;
|
|
assign buffer_wr = db_state == DB_check_xfer_write && buffer_matches_DMA;
|
|
|
|
assign ide_read_req = db_state == DB_read_new_page;
|
|
assign ide_write_req = db_state == DB_write_old_page;
|
|
|
|
//
|
|
// RF08 state
|
|
//
|
|
assign ADC = buffer_matches_DMA;
|
|
|
|
// fake the photocell sensor
|
|
always @(posedge clk)
|
|
if (reset)
|
|
photocell_counter <= 0;
|
|
else
|
|
begin
|
|
photocell_counter <= photocell_counter + 1;
|
|
end
|
|
|
|
assign PCA = photocell_counter < 16;
|
|
assign DRE = ~db_done;
|
|
|
|
/* we don't support write lock */
|
|
always @(posedge clk)
|
|
if (reset)
|
|
begin
|
|
WLS <= 1'b0;
|
|
end
|
|
|
|
endmodule
|
|
|