Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Original Statemachine documentation #8

Closed
MHeinrichs opened this issue Sep 10, 2024 · 5 comments
Closed

Original Statemachine documentation #8

MHeinrichs opened this issue Sep 10, 2024 · 5 comments
Labels
documentation Improvements or additions to documentation

Comments

@MHeinrichs
Copy link

Hi the commodore statemachine-schematics are derived from several test-files, describing the state-transistions in some kind of basic-ish dialect.

Here they are, if they are of any use:
scsi (newer version?!?)
`-- __________________________________________

-- DMAC SCSI Interface state machine
-- __________________________________________

INPUTS:
DREQ_ -- data request from SCSI chip
FIFOFULL
FIFOEMPTY
BOEQ3 -- byte offset = 3
DMADIR -- DMA direction (low=write to DISK)
CPUREQ -- CPU request. Cpu wants to read/write a SCSI register
DSACK_ -- DSACK to CPU
RIFIFO_o -- Request to Increment FIFO counter (when this becomes invalid, then the increment was done)
RDFIFO_o -- Request to Decrement FIFO counter ( " )
RW -- Read/Write line from 68030.
;
OUTPUTS:
DACK -- Data Acknowledge to SCSI chip
INCBO -- increment byte offset
INCNI -- increment pointer to next longword in
INCNO -- increment pointer to next longword out
RE -- read enable to SCSI chip
WE -- write enable to SCSI chip
SCSI_CS -- Chip select to SCSI chip
SET_DSACK -- Make DSACK active
RIFIFO_d -- Request to Increment FIFO counter (out to CPU state machine)
RDFIFO_d -- Request to decrement FIFO counter ( " )
S2F -- Turn input buffers on so data can be read from SCSI chip (SCSI to FIFO)
F2S -- Turn output buffers on so data can be written to SCSI chip (FIFO to SCSI)
S2CPU -- Turn internal data buffers on to route data to be read from SCSI to the CPU
CPU2S -- Turn internal data buffers on to route data to be written to SCSI from the CPU
;

-- |<--------- 5 clocks per byte -------- >|

-- S0,7 | S1,8 | S2,9 | S3,10 | S6,13 | S0,7 | S1,8 | S2,9 |
-- ___ ___ ___ ___ ___ ___ ___ ___
-- CPUCLK / _/ _/ _/ _/ _/ _/ _/
-- _____________________ ___________
-- *DREQ // _
_____//
-- ___ _______________________ ___
-- *DSACK _/ _/
-- ___ _______________________ ___
-- *WE _/ _/
-- ___ _______________________ ___
-- *RE _/ _/

-- DMA read from SCSI device

s0: case (CPUREQ DREQ_ FIFOFULL DMADIR RIFIFO_o)
0 1 ? 1 ? => s0; -- no data to get.
0 0 0 1 0 => s1( RE DACK S2F); -- data ready, & room, & no incr. req. pending, so read it
0 0 0 1 1 => s0; -- data ready, and room, but increment req is pending, so wait
0 0 1 1 ? => s0; -- data ready, but no place to put it.
0 ? ? 0 ? => s7; -- go to DMA write mode
1 ? ? ? ? => s50; -- CPU wants access to a SCSI register
endcase => ANY;

s1: if FIFOFULL then s2 (INCNI INCNO) -- This can NEVER happen! It is here to keep INCNI/RIFIFO_d & INCNO/RDFIFO_d
-- output logic different (gfl to dfd converter would eliminate one).
else s2(RE DACK S2F); -- RE to scsi chip (This is the only line really necessary)
-- NOTE: Ending edge of RE is used to latch data in. SCSI spec says that it holds
-- read data valid for min of 5 nsecs, which SHOULD be enough hold time to latch it in.
-- Also, ensure BYTE OFFSET ptr & NEXT IN ptr don't change before data is latched in.

s2: goto s3(S2F);

s3: if BOEQ3 then s6(RIFIFO_d INCBO INCNI) -- if last byte in lword, increment the pointer
else s6(INCBO);

s6: goto s0;

-- DMA write to SCSI device

s7: case (CPUREQ DREQ_ FIFOEMPTY DMADIR RDFIFO_o)
0 1 ? 0 ? => s7; -- SCSI chip doesn't want data yet.
0 0 1 0 ? => s7; -- SCSI ready for data, but nothing in FIFO yet.
0 0 0 0 0 => s8(DACK WE F2S); -- Data to send to SCSI, & no decrement pending, so send it
0 0 0 0 1 => s7; -- Data to send to SCSI, but decrement is pending, so wait
0 ? ? 1 ? => s0; -- go to DMA read mode
1 ? ? ? ? => s50; -- CPU wants access to a SCSI register
endcase => ANY;

s8: goto s9(DACK WE F2S);

s9: goto s10(F2S);

s10: if BOEQ3 then s13(RDFIFO_d INCBO INCNO) -- if last byte in lword, increment the pointer
else s13(INCBO);

s13: goto s7;

-- CPU access to the SCSI internal registers

s50: case (RW)
0 => s51(SCSI_CS WE CPU2S); -- Write to SCSI chip register.
1 => s60(SCSI_CS RE S2CPU); -- Read from SCSI chip register.
endcase => ANY;

s51: goto s52(SCSI_CS WE CPU2S); -- Write to SCSI register
s52: goto s53(SCSI_CS WE CPU2S);
s53: goto s54(SCSI_CS CPU2S SET_DSACK);
s54: goto s55;
s55: if NOT DSACK_ then s55 -- Wait for write cycle to finish
else s0;

s60: goto s61(SCSI_CS RE S2CPU); -- Read from SCSI register
s61: goto s62(SCSI_CS RE S2CPU); -- Read from SCSI register
s62: goto s63(SCSI_CS RE S2CPU); -- Read from SCSI register

s63: goto s64(SCSI_CS RE S2CPU SET_DSACK);
s64: goto s65( S2CPU );
s65: if NOT DSACK_ then s65(S2CPU) -- Wait for read cycle to finish
else s0;
`

Scsi (older version?!?!)
`
-- __________________________________________

-- DMAC SCSI Interface state machine
-- __________________________________________

INPUTS:
DREQ_ -- data request from SCSI chip
FIFOFULL
FIFOEMPTY
BOEQ3 -- byte offset = 3
DMADIR -- DMA direction (low=write to DISK)
CPUREQ -- CPU request. Cpu wants to read/write a SCSI register
DSACK_ -- DSACK to CPU
RIFIFO_o -- Request to Increment FIFO counter (when this becomes invalid, then the increment was done)
RDFIFO_o -- Request to Decrement FIFO counter ( " )
RW -- Read/Write line from 68030.
;
OUTPUTS:
DACK -- Data Acknowledge to SCSI chip
INCBO -- increment byte offset
INCNI -- increment pointer to next longword in
INCNO -- increment pointer to next longword out
RE -- read enable to SCSI chip
WE -- write enable to SCSI chip
SCSI_CS -- Chip select to SCSI chip
SET_DSACK -- Make DSACK active
RIFIFO_d -- Request to Increment FIFO counter (out to CPU state machine)
RDFIFO_d -- Request to decrement FIFO counter ( " )
S2F -- Turn input buffers on so data can be read from SCSI chip (SCSI to FIFO)
F2S -- Turn output buffers on so data can be written to SCSI chip (FIFO to SCSI)
S2CPU -- Turn internal data buffers on to route data to be read from SCSI to the CPU
CPU2S -- Turn internal data buffers on to route data to be written to SCSI from the CPU
;

-- DMA read from SCSI device

s0: case (CPUREQ DREQ_ FIFOFULL DMADIR RIFIFO_o)
0 1 ? 1 ? => s0; -- no data to get.
0 0 0 1 0 => s1(DACK); -- data ready, and room, and no increment req. pending, so read it
0 0 0 1 1 => s0; -- data ready, and room, but increment req is pending, so wait
0 0 1 1 ? => s0; -- data ready, but no place to put it.
0 ? ? 0 ? => s7; -- go to DMA write mode
1 ? ? ? ? => s50; -- CPU wants access to a SCSI register
endcase => ANY;

s1: if FIFOFULL then s2 (INCNI INCNO) -- This can NEVER happen! It is here to keep INCNI/RIFIFO_d & INCNO/RDFIFO_d
-- output logic different (gfl to dfd converter would eliminate one).
else s2(RE DACK S2F); -- RE to scsi chip (This is the only line really necessary)

s2: goto s3(RE DACK S2F);

s3: goto s6(RE DACK S2F); -- NOTE: Ending edge of RE is used to latch data in. SCSI spec says that it holds
-- read data valid for min of 10 nsecs, which SHOULD be enough hold time to latch it in.
-- Also, ensure BYTE OFFSET ptr & NEXT IN ptr don't change before data is latched in.

s6: if BOEQ3 then s0(RIFIFO_d INCBO INCNI S2F) -- if last byte in lword, incr the ptrs.
else s0(INCBO S2F);

-- DMA write to SCSI device

s7: case (CPUREQ DREQ_ FIFOEMPTY DMADIR RDFIFO_o)
0 1 ? 0 ? => s7; -- SCSI chip doesn't want data yet.
0 0 1 0 ? => s7; -- SCSI ready for data, but nothing in FIFO yet.
0 0 0 0 0 => s8(DACK); -- Data to send to SCSI, and no decrement pending, so send it
0 0 0 0 1 => s7; -- Data to send to SCSI, but decrement is pending, so wait
0 ? ? 1 ? => s0; -- go to DMA read mode
1 ? ? ? ? => s50; -- CPU wants access to a SCSI register
endcase => ANY;

s8: goto s9(DACK WE F2S);

s9: goto s10(DACK WE F2S);

s10: goto s13(DACK WE F2S);

s13: if BOEQ3 then s7(RDFIFO_d INCBO INCNO F2S) -- if last byte in lword, incr the ptr.
else s7(INCBO F2S);

-- CPU access to the SCSI internal registers

s50: case (RW)
0 => s51(SCSI_CS WE CPU2S); -- Write to SCSI chip register.
1 => s60(SCSI_CS RE S2CPU); -- Read from SCSI chip register.
endcase => ANY;

s51: goto s52(SCSI_CS WE CPU2S); -- Write to SCSI register
s52: goto s53(SCSI_CS WE CPU2S);
s53: goto s54(SCSI_CS CPU2S SET_DSACK);
s54: goto s55;
s55: if NOT DSACK_ then s55 -- Wait for write cycle to finish
else s0;

s60: goto s61(SCSI_CS RE S2CPU); -- Read from SCSI register
s61: goto s62(SCSI_CS RE S2CPU); -- Read from SCSI register
s62: goto s63(SCSI_CS RE S2CPU); -- Read from SCSI register

s63: goto s64(SCSI_CS RE S2CPU SET_DSACK);
s64: goto s65( S2CPU );
s65: if NOT DSACK_ then s65(S2CPU) -- Wait for read cycle to finish
else s0;
`

registers:
`-- _____________________________________________________

-- DMAC: CPU to DMAC/SCSI registers state machine
-- _____________________________________________________

INPUTS:
RW -- 68030 Read/Write line
DECODE -- Address space for DMAC registers AND AS (clocked in on opposite edge of state clock)
DSACK0
DSACK1
;
OUTPUTS:
SET_DSACK0
SET_DSACK1
;

s0: case (AS DECODE)
1 ? => s0;
0 0 => s0;
0 1 => s5;
endcase => ANY;

s5:
`

CPU:
`-- ____________________________________

-- DMAC CPU Interface state machine
-- ____________________________________

-- 9/12/90 Remove INCNO output because it is the same as DECFIFO

INPUTS:
FIFOFULL -- FIFOCNT = 8
FIFOEMPTY -- FIFOCNT = 0
DMADIR -- DMA direction (low=write to DISK)
A1 -- DMA address bit
CYCLEDONE -- *AS,DSACKx,*STERM and *BGACK are all high
BGRANT_ -- bus grant
STERM_
DSACK -- If either DSACKx signal is low on a falling CPUCLK edge, this signal goes valid (speed critical).
DSACK1_ DSACK0_ -- speed critical
FLUSHFIFO -- Flush the FIFO. When flushed an interrupt should occur.
DREQ_ -- data request from SCSI chip
-- RDFIFO -- Request to Decrement FIFO CouNTer. From the SCSI state machine.
-- Keeps the 2 state machines from inc'ing & dec'ing the counter at the same time.
-- RIFIFO -- Request to Increment FIFO CouNTer. From the SCSI state machine.
LASTWORD -- If FLUSHing & FIFOEMPTY & !BOEQ0 then we got 1 last lonely word to send to the CPU
DMAENA -- DMA is turned on
BOEQ3 -- Byte offset = 3. 3 valid bytes are in the current FIFO entry.
;
OUTPUTS:
INCNI -- increment pointer to next longword in
-- INCNO -- increment pointer to next longword out (same output as DECFIFO)
BREQ -- bus request
BGACK -- bus grant acknowledge
SIZE1 -- size1,size0=00 lword ; =10 word
PAS -- pre adress strobe (gets clocked out on falling cpuclk)
PDS -- pre data strobe " " " " " "
F2CPUL -- Send D0-D15 out D0-D15
F2CPUH -- Send D16-D31 out D16-D31
BRIDGEOUT -- Send D0-D15 out D16-D31
PLLW PLHW -- pre Latch Low/High Words. DMA data being written to FIFO. Latch actually occurs on falling edge of CLK
INCFIFO -- INCrement the FIFO counter.
DECFIFO -- DECrement the FIFO counter.
STOPFLUSH -- Turn the FLUSHFIFO bit off.
DIEH -- Data Input Enable for High word.
DIEL -- Data Input Enable for Low word.
BRIDGEIN -- Send D16-D31 inputs to D0-D15 input lines
;


-- DMA read from FIFO writing to CPU


s0: case (DMAENA FIFOFULL DMADIR FLUSHFIFO FIFOEMPTY LASTWORD)
0 ? ? ? ? ? => s0; -- DMA is not turned on
1 0 1 0 ? ? => s0; -- FIFO not yet full
1 0 1 1 0 ? => s1(BREQ); -- flush whatever's there
1 0 1 1 1 0 => s0(STOPFLUSH); -- Nothing to flush
1 0 1 1 1 1 => s1(BREQ); -- One last lonely word to send to CPU
1 1 1 ? ? ? => s1(BREQ); -- time to dump the FIFO to CPU
1 ? 0 ? ? ? => s20; -- go to DMA write mode
endcase => ANY;

s1: case (BGRANT_ CYCLEDONE A1 LASTWORD BOEQ3) -- wait for the bus
1 ? ? ? ? => s1(BREQ);
0 0 ? ? ? => s1(BREQ);
0 1 0 0 ? => s2(BREQ BGACK); -- start, lword aligned
0 1 0 1 0 => s10(BREQ BGACK STOPFLUSH); -- start, very last byte/word, so write word
0 1 0 1 1 => s2(BREQ BGACK STOPFLUSH); -- start, 3 bytes left so write lword
0 1 1 ? ? => s6(BREQ BGACK); -- start, lword unaligned (only happen very first time)
endcase => ANY;

----- Attempt to write the entire aligned longword -----

s2: goto s3(BGACK PAS F2CPUH F2CPUL);

s3: if NOT STERM_ then s15(BGACK F2CPUH F2CPUL DECFIFO) -- first cycle of a CPU access
else s4(BGACK PAS PDS F2CPUH F2CPUL);

s4: case (STERM_ DSACK DSACK1_ DSACK0_) -- wait for cycle to end
0 ? ? ? => s15(BGACK F2CPUH F2CPUL DECFIFO); -- 32 bit cycle terminated
1 0 ? ? => s4(BGACK PAS PDS F2CPUH F2CPUL); -- Cycle not yet terminated
1 1 0 0 => s15(BGACK F2CPUH F2CPUL DECFIFO); -- 32 bit cycle terminated
1 1 0 1 => s6(BGACK F2CPUH F2CPUL); -- 16 bit cycle terminated, so now write the 2nd word
-- 1 1 1 0 8 bit ports not supported
-- 1 1 1 1 impossible (I hope...)
endcase => ANY;

----- Write the 16 bit odd word value -----

s6: goto s7(BGACK PAS BRIDGEOUT F2CPUL SIZE1); -- start a CPU cycle (lword unaligned)

s7: if NOT STERM_ then s15(BGACK BRIDGEOUT F2CPUL SIZE1 DECFIFO) -- first cycle of CPU access
else s8(BGACK PAS PDS BRIDGEOUT F2CPUL SIZE1);

s8: case (STERM_ DSACK DSACK1_ DSACK0_) -- wait for cycle to end
0 ? ? ? => s15(BGACK BRIDGEOUT F2CPUL SIZE1 DECFIFO); -- 32 bit cycle terminated
1 0 ? ? => s8(BGACK PAS PDS BRIDGEOUT F2CPUL SIZE1); -- Cycle not yet terminated
1 1 0 0 => s15(BGACK BRIDGEOUT F2CPUL SIZE1 DECFIFO); -- 32 bit cycle terminated
1 1 0 1 => s15(BGACK BRIDGEOUT F2CPUL SIZE1 DECFIFO); -- 16 bit cycle terminated
endcase => ANY;

----- Special case to write last byte or word (not a full longword) to the CPU during a FLUSH of the FIFO -----
-- (if only a byte left, then it is padded with an extra byte when the word is written out)

s10: goto s11(BGACK PAS F2CPUH F2CPUL SIZE1);

s11: if NOT STERM_ then s15(BGACK F2CPUH F2CPUL SIZE1) -- first cycle of a CPU access
else s12(BGACK PAS PDS F2CPUH F2CPUL SIZE1);

s12: case (STERM_ DSACK DSACK1_ DSACK0_) -- wait for cycle to end
0 ? ? ? => s15(BGACK F2CPUH F2CPUL SIZE1); -- 32 bit cycle terminated
1 0 ? ? => s12(BGACK PAS PDS F2CPUH F2CPUL SIZE1); -- Cycle not yet terminated
1 1 0 0 => s15(BGACK F2CPUH F2CPUL SIZE1); -- 32 bit cycle terminated
1 1 0 1 => s15(BGACK F2CPUH F2CPUL SIZE1); -- 16 bit cycle terminated
endcase => ANY;

----- Check if there is anything left to do -----

s15: case (FIFOEMPTY LASTWORD BOEQ3) -- check if more to do
1 0 ? => letgo(BGACK STOPFLUSH); -- no more left
1 1 0 => s11(BGACK PAS F2CPUH F2CPUL SIZE1 STOPFLUSH); -- very last byte/word to do
1 1 1 => s3(BGACK PAS F2CPUH F2CPUL STOPFLUSH); -- very last 3 bytes left,so write LWORD
0 ? ? => s3(BGACK PAS F2CPUH F2CPUL); -- start another CPU cycle
endcase => ANY;


-- DMA read from CPU writing to FIFO


-- How to keep DMA from reading extra data from CPU on its last FIFO fill?? Beats says its OK if it does

s20: case (DMAENA DMADIR FIFOEMPTY DREQ_)
0 ? ? ? => s20; -- DMA is not turned on
1 0 0 ? => s20; -- FIFO not empty yet
1 0 1 1 => s20; -- Don't put data in FIFO 'til SCSI asks for more
1 0 1 0 => s21; -- Time to put data in FIFO
1 1 ? ? => s0; -- go to DMA read mode
endcase => ANY;

s21: case (BGRANT_ CYCLEDONE A1) -- wait for the bus
1 ? ? => s21(BREQ);
0 0 ? => s21(BREQ);
0 1 0 => s22(BREQ BGACK); -- start, lword aligned
0 1 1 => s30(BREQ BGACK); -- start, lword unaligned (can only happen very first time)
endcase => ANY;

-- Attempt to read the entire aligned longword

s22: goto s23(BGACK PAS PDS PLHW PLLW DIEL DIEH); -- start CPU cycle (lword aligned)

s23: if NOT STERM_ then s35(BGACK INCFIFO DIEL DIEH) -- first cycle of a CPU access
else s24(BGACK PAS PDS PLHW PLLW DIEL DIEH);

s24: case (STERM_ DSACK DSACK1_ DSACK0_) -- wait for cycle to end
0 ? ? ? => s35(BGACK INCFIFO DIEL DIEH); -- 32 bit cycle terminated
1 0 ? ? => s24(BGACK PAS PDS PLHW PLLW DIEL DIEH); -- Cycle not yet terminated
1 1 0 0 => s35(BGACK INCFIFO DIEL DIEH); -- 32 bit cycle terminated
1 1 0 1 => s26(BGACK DIEH); -- 16 bit cycle terminated
endcase => ANY;

-- Responded as 16 bits only, so re-Read the 16 bit oddword value from this 16 bit port

s26: goto s27(BGACK PAS PDS PLLW SIZE1 DIEH BRIDGEIN); -- start CPU cycle (lword unaligned)

s27: goto s28(BGACK PAS PDS PLLW SIZE1 DIEH BRIDGEIN); -- No need to check STERM 'cause if it was a 32 bit port we never
-- could have got here...

s28: if NOT DSACK then s28(BGACK PAS PDS PLLW SIZE1 DIEH BRIDGEIN) -- Cycle not yet terminated
else s35(BGACK SIZE1 DIEH BRIDGEIN INCFIFO); -- cycle terminated (can assume it was via 16 bit DSACK)

-- Special case for very first odd word access of a DMA. We don't know which half of the data bus will have the
-- data. If 32 bit port then word will be on D0-D15. If 16 bit port then word will be on D16-D31. Assume it is
-- a 32 bit port. If it responds as a 16 bit port, then we have to re-direct the latched input data on D16-D31 to
-- D0-D15 and then latch it into the FIFO. This adds a couple of extra cycles before *AS is asserted to begin the
-- next DMA read. However, this only happens at worst case 1 time for an entire DMA.

s30: goto s31(BGACK PAS PDS PLLW SIZE1 DIEH DIEL); -- start CPU cycle (lword unaligned)

s31: if NOT STERM_ then s35(BGACK SIZE1 DIEH DIEL INCFIFO) -- first cycle of a CPU access
else s32(BGACK PAS PDS PLLW SIZE1 DIEH DIEL);

s32: case (STERM_ DSACK DSACK1_ DSACK0_) -- wait for cycle to end
0 ? ? ? => s35(BGACK SIZE1 DIEH DIEL INCFIFO); -- 32 bit cycle terminated
1 0 ? ? => s32(BGACK PAS PDS PLLW SIZE1 DIEH DIEL); -- Cycle not yet terminated
1 1 0 0 => s35(BGACK SIZE1 DIEH DIEL INCFIFO); -- 32 bit cycle terminated
1 1 0 1 => s33(BGACK PLLW SIZE1); -- 16 bit cycle terminated, so gotta bridge & such...
endcase => ANY;

s33: goto s34(BGACK PLLW BRIDGEIN);

s34: goto s35(BGACK BRIDGEIN INCFIFO);

-- Check if there is any more room in the FIFO for more data

s35: case (FIFOFULL) -- check if room in FIFO for more
1 => letgo(BGACK INCNI); -- no more room in FIFO
0 => s23(BGACK PAS PDS PLHW PLLW DIEL DIEH INCNI); -- start another CPU cycle
endcase => ANY;

letgo: goto s0;
CPU-Latch:
-- ____________________________________

-- DMAC CPU Interface state machine
-- ____________________________________

INPUTS:
FIFOFULL -- FIFOCNT = 8
FIFOEMPTY -- FIFOCNT = 0
FIFOAFULL -- FIFO Almost FULL. FIFOCNT = 7.
FIFORST -- Reset the FIFO (FIFOCNT=0,NEXTIN,FIRSTOUT=0)
DMADIR -- DMA direction (low=write to DISK)
A1 -- DMA address bit
CYCLEDONE -- *AS,DSACKx,*STERM and *BGACK are all high
BGRANT -- bus grant
STERM
DSACK1 DSACK0 -- get clocked in with falling CPUCLK (speed may be critical...)
FLUSHFIFO -- Flush the FIFO
DREQ -- data request from SCSI chip
RDFIFO -- Request to Decrement FIFO CouNTer. From the SCSI state machine.
-- Keeps the 2 state machines from inc'ing & dec'ing the counter at the same time.
RIFIFO -- Request to Increment FIFO CouNTer. From the SCSI state machine.
FIRSTIN
;
OUTPUTS:
INCNI -- increment pointer to next longword in
INCNO -- increment pointer to next longword out
BREQ -- bus request
BGACK -- bus grant acknowledge
SIZE1 -- size1,size0=00 lword ; =10 word
PAS -- pre adress strobe (gets clocked out on falling cpuclk)
PDS -- pre data strobe " " " " " "
F2CPUH -- Send D16-D31 out D16-D31
BRIDGE -- Send D0-D15 out D16-D31
LOD -- Latch output data from FIFO
PLLW PLHW -- pre Latch Low/High Words. DMA data being written to FIFO. Latch actually occurs on falling edge of CLK
FIRSTOUT -- Indicates that this is the first transfer of this DMA sequence. Only actually needed on
-- the very first DMA sequence IF the first transfer is not aligned on a longword. It is used
-- to allow the pointer to the next FIFO entry to be output to be incremented at the proper time.
INCFIFO -- INCrement the FIFO counter.
DECFIFO -- DECrement the FIFO counter.
;

-- DMA read from FIFO to CPU

s0: case (FIFOFULL DMADIR FLUSHFIFO FIFOEMPTY)
0 1 0 ? => s0; -- FIFO not yet full
0 1 1 0 => s1(BREQ); -- flush whatever's there
0 1 1 1 => s0; -- Nothing to flush
1 1 ? ? => s1(BREQ); -- time to dump the FIFO to CPU
? 0 ? ? => s20; -- go to DMA write mode
endcase => ANY;

s1: case (BGRANT CYCLEDONE A1) -- wait for the bus
0 ? ? => s1(BREQ);
1 0 ? => s1(BREQ);
1 1 0 => s2(BREQ BGACK); -- start, lword aligned
1 1 1 => s6(BREQ BGACK FIRSTOUT LOD DECFIFO); -- start, lword unaligned
endcase => ANY;

s2: goto s3(BGACK PAS PDS F2CPUH LOD DECFIFO);

s3: if STERM then s5(BGACK F2CPUH INCNO) -- first cycle of a CPU access
else s4(BGACK PAS PDS F2CPUH INCNO);

s4: case (STERM DSACK1 DSACK0) -- wait for cycle to end
0 0 0 => s4(BGACK PAS PDS F2CPUH); -- Cycle not yet terminated
1 0 0 => s5(BGACK F2CPUH); -- 32 bit cycle terminated
0 1 1 => s5(BGACK F2CPUH); -- 32 bit cycle terminated
0 1 0 => s6(BGACK F2CPUH); -- 16 bit cycle terminated
endcase => ANY;

s5: case (FIFOEMPTY) -- check if more to do
0 => letgo(BGACK); -- no more left
1 => s3(BGACK PAS PDS F2CPUH DECFIFO LOD); -- do another one
endcase => ANY;

s6: if FIRSTIN then s7(BGACK PAS PDS BRIDGE SIZE1 INCNO) -- start a CPU cycle (lword unaligned)
else s7(BGACK PAS PDS BRIDGE SIZE1); -- If this is the FIRST access of the DMA beinning
-- on an unaligned lword then increment the pointer.

s7: if STERM then s9(BGACK BRIDGE SIZE1) -- first cycle of CPU access
else s8(BGACK PAS PDS BRIDGE SIZE1);

s8: case (STERM DSACK1 DSACK0) -- wait for cycle to end
0 0 0 => s8(BGACK PAS PDS BRIDGE SIZE1); -- Cycle not yet terminated
1 0 0 => s9(BGACK BRIDGE SIZE1); -- 32 bit cycle terminated
0 1 1 => s9(BGACK BRIDGE SIZE1); -- 32 bit cycle terminated
0 1 0 => s9(BGACK BRIDGE SIZE1); -- 16 bit cycle terminated
endcase => ANY;

s9: case (FIFOEMPTY) -- check if more to do
0 => letgo(BGACK); -- no more left
1 => s3(BGACK PAS PDS F2CPUH DECFIFO LOD); -- start another CPU cycle
endcase => ANY;

-- DMA write from CPU to FIFO
-- How to keep DMA from reading extra data from CPU on its last FIFO fill??
s20: case (DMADIR FIFOEMPTY DREQ)
0 0 ? => s20; -- FIFO not empty yet
0 1 0 => s20; -- Don't put data in FIFO 'til SCSI asks for more
0 1 1 => s21; -- Time to put data in FIFO
1 ? ? => s0; -- go to DMA read mode
endcase => ANY;

s21: case (BGRANT CYCLEDONE A1) -- wait for the bus
0 ? ? => s21(BREQ);
1 0 ? => s21(BREQ);
1 1 0 => s22(BREQ BGACK); -- start, lword aligned
1 1 1 => s26(BREQ BGACK); -- start, lword unaligned
endcase => ANY;

s22: goto s23(BGACK PAS);

s23: if STERM then s25(BGACK PLHW PLLW) -- first cycle of a CPU access
else s24(BGACK PAS PDS);

s24: case (STERM DSACK1 DSACK0) -- wait for cycle to end
0 0 0 => s24(BGACK PAS PDS); -- Cycle not yet terminated
1 0 0 => s25(BGACK PLHW PLLW); -- 32 bit cycle terminated
0 1 1 => s25(BGACK PLHW PLLW); -- 32 bit cycle terminated
0 1 0 => s26(BGACK PLHW PLLW); -- 16 bit cycle terminated
endcase => ANY;

s25: case (FIFOAFULL) -- check if room in FIFO for more
1 => letgo(BGACK INCFIFO INCNI); -- no more room
0 => s23(BGACK PAS PDS INCFIFO INCNI); -- do another one
endcase => ANY;

s26: goto s27(BGACK PAS SIZE1); -- start a CPU cycle (lword unaligned)

s27: if STERM then s29(BGACK SIZE1 PLLW) -- first cycle of a CPU access
else s28(BGACK PAS PDS SIZE1);

s28: case (STERM DSACK1 DSACK0) -- wait for cycle to end
0 0 0 => s28(BGACK PAS PDS SIZE1); -- Cycle not yet terminated
1 0 0 => s29(BGACK SIZE1 PLLW); -- 32 bit cycle terminated
0 1 1 => s29(BGACK SIZE1 PLLW); -- 32 bit cycle terminated
0 1 0 => s29(BGACK SIZE1 PLLW); -- 16 bit cycle terminated
endcase => ANY;
-- WHEN to increment Next In (INCNI) ????????????????????????????????????
s29: case (FIFOAFULL) -- check if room in FIFO for more
1 => letgo(BGACK INCFIFO INCNI); -- no more room in FIFO
0 => s3(BGACK PAS PDS INCFIFO INCNI); -- start another CPU cycle
endcase => ANY;

letgo: goto s0;
`

test:
`
-- ____________________________________

-- DMAC CPU Interface state machine
-- ____________________________________

-- 9/12/90 Remove INCNO output because it is the same as DECFIFO

INPUTS:
FIFOFULL -- FIFOCNT = 8
FIFOEMPTY -- FIFOCNT = 0
DMADIR -- DMA direction (low=write to DISK)
A1 -- DMA address bit
CYCLEDONE -- *AS,DSACKx,*STERM and *BGACK are all high
BGRANT_ -- bus grant
STERM_
DSACK -- If either DSACKx signal is low on a falling CPUCLK edge, this signal goes valid (speed critical).
DSACK1_ DSACK0_ -- speed critical
FLUSHFIFO -- Flush the FIFO. When flushed an interrupt should occur.
DREQ_ -- data request from SCSI chip
-- RDFIFO -- Request to Decrement FIFO CouNTer. From the SCSI state machine.
-- Keeps the 2 state machines from inc'ing & dec'ing the counter at the same time.
-- RIFIFO -- Request to Increment FIFO CouNTer. From the SCSI state machine.
LASTWORD -- If FLUSHing & FIFOEMPTY & !BOEQ0 then we got 1 last lonely word to send to the CPU
DMAENA -- DMA is turned on
BOEQ3 -- Byte offset = 3. 3 valid bytes are in the current FIFO entry.
;
OUTPUTS:
INCNI -- increment pointer to next longword in
-- INCNO -- increment pointer to next longword out (same output as DECFIFO)
BREQ -- bus request
BGACK -- bus grant acknowledge
SIZE1 -- size1,size0=00 lword ; =10 word
PAS -- pre adress strobe (gets clocked out on falling cpuclk)
PDS -- pre data strobe " " " " " "
F2CPUL -- Send D0-D15 out D0-D15
F2CPUH -- Send D16-D31 out D16-D31
BRIDGEOUT -- Send D0-D15 out D16-D31
PLLW PLHW -- pre Latch Low/High Words. DMA data being written to FIFO. Latch actually occurs on falling edge of CLK
INCFIFO -- INCrement the FIFO counter.
DECFIFO -- DECrement the FIFO counter.
STOPFLUSH -- Turn the FLUSHFIFO bit off.
DIEH -- Data Input Enable for High word.
DIEL -- Data Input Enable for Low word.
BRIDGEIN -- Send D16-D31 inputs to D0-D15 input lines
;


-- DMA read from FIFO writing to CPU


s0: case (DMAENA FIFOFULL DMADIR FLUSHFIFO FIFOEMPTY LASTWORD)
0 ? ? ? ? ? => s0; -- DMA is not turned on
1 0 1 0 ? ? => s0; -- FIFO not yet full
1 0 1 1 0 ? => s1(BREQ); -- flush whatever's there
1 0 1 1 1 0 => s0(STOPFLUSH); -- Nothing to flush
1 0 1 1 1 1 => s1(BREQ); -- One last lonely word to send to CPU
1 1 1 ? ? ? => s1(BREQ); -- time to dump the FIFO to CPU
1 ? 0 ? ? ? => s20; -- go to DMA write mode
endcase => ANY;

s1: case (BGRANT_ CYCLEDONE A1 LASTWORD BOEQ3) -- wait for the bus
1 ? ? ? ? => s1(BREQ);
0 0 ? ? ? => s1(BREQ);
0 1 0 0 ? => s2(BREQ BGACK); -- start, lword aligned
0 1 0 1 0 => s10(BREQ BGACK STOPFLUSH); -- start, very last byte/word, so write word
0 1 0 1 1 => s2(BREQ BGACK STOPFLUSH); -- start, 3 bytes left so write lword
0 1 1 ? ? => s6(BREQ BGACK); -- start, lword unaligned (only happen very first time)
endcase => ANY;

----- Attempt to write the entire aligned longword -----

s2: goto s3(BGACK PAS F2CPUH F2CPUL);

s3: if NOT STERM_ then s15(BGACK F2CPUH F2CPUL DECFIFO) -- first cycle of a CPU access
else s4(BGACK PAS PDS F2CPUH F2CPUL);

s4: case (STERM_ DSACK DSACK1_ DSACK0_) -- wait for cycle to end
0 0 ? ? => s15(BGACK F2CPUH F2CPUL DECFIFO); -- 32 bit cycle terminated
1 0 ? ? => s4(BGACK PAS PDS F2CPUH F2CPUL); -- Cycle not yet terminated
? 1 0 0 => s15(BGACK F2CPUH F2CPUL DECFIFO); -- 32 bit cycle terminated
? 1 0 1 => s6(BGACK F2CPUH F2CPUL); -- 16 bit cycle terminated, so now write the 2nd word
-- 1 1 1 0 8 bit ports not supported
-- 1 1 1 1 impossible (I hope...)
endcase => ANY;

----- Write the 16 bit odd word value -----

s6: goto s7(BGACK PAS BRIDGEOUT F2CPUL SIZE1); -- start a CPU cycle (lword unaligned)

s7: if NOT STERM_ then s15(BGACK BRIDGEOUT F2CPUL SIZE1 DECFIFO) -- first cycle of CPU access
else s8(BGACK PAS PDS BRIDGEOUT F2CPUL SIZE1);

s8: case (STERM_ DSACK DSACK1_ DSACK0_) -- wait for cycle to end
0 0 ? ? => s15(BGACK BRIDGEOUT F2CPUL SIZE1 DECFIFO); -- 32 bit cycle terminated
1 0 ? ? => s8(BGACK PAS PDS BRIDGEOUT F2CPUL SIZE1); -- Cycle not yet terminated
? 1 0 0 => s15(BGACK BRIDGEOUT F2CPUL SIZE1 DECFIFO); -- 32 bit cycle terminated
? 1 0 1 => s15(BGACK BRIDGEOUT F2CPUL SIZE1 DECFIFO); -- 16 bit cycle terminated
endcase => ANY;

----- Special case to write last byte or word (not a full longword) to the CPU during a FLUSH of the FIFO -----
-- (if only a byte left, then it is padded with an extra byte when the word is written out)

s10: goto s11(BGACK PAS F2CPUH F2CPUL SIZE1);

s11: if NOT STERM_ then s15(BGACK F2CPUH F2CPUL SIZE1) -- first cycle of a CPU access
else s12(BGACK PAS PDS F2CPUH F2CPUL SIZE1);

s12: case (STERM_ DSACK DSACK1_ DSACK0_) -- wait for cycle to end
0 0 ? ? => s15(BGACK F2CPUH F2CPUL SIZE1); -- 32 bit cycle terminated
1 0 ? ? => s12(BGACK PAS PDS F2CPUH F2CPUL SIZE1); -- Cycle not yet terminated
? 1 0 0 => s15(BGACK F2CPUH F2CPUL SIZE1); -- 32 bit cycle terminated
? 1 0 1 => s15(BGACK F2CPUH F2CPUL SIZE1); -- 16 bit cycle terminated
endcase => ANY;

----- Check if there is anything left to do -----

s15: case (FIFOEMPTY LASTWORD BOEQ3) -- check if more to do
1 0 ? => letgo(BGACK STOPFLUSH); -- no more left
1 1 0 => s11(BGACK PAS F2CPUH F2CPUL SIZE1 STOPFLUSH); -- very last byte/word to do
1 1 1 => s3(BGACK PAS F2CPUH F2CPUL STOPFLUSH); -- very last 3 bytes left,so write LWORD
0 ? ? => s3(BGACK PAS F2CPUH F2CPUL); -- start another CPU cycle
endcase => ANY;


-- DMA read from CPU writing to FIFO


-- How to keep DMA from reading extra data from CPU on its last FIFO fill?? Beats says its OK if it does

s20: case (DMAENA DMADIR FIFOEMPTY DREQ_)
0 ? ? ? => s20; -- DMA is not turned on
1 0 0 ? => s20; -- FIFO not empty yet
1 0 1 1 => s20; -- Don't put data in FIFO 'til SCSI asks for more
1 0 1 0 => s21; -- Time to put data in FIFO
1 1 ? ? => s0; -- go to DMA read mode
endcase => ANY;

s21: case (BGRANT_ CYCLEDONE A1) -- wait for the bus
1 ? ? => s21(BREQ);
0 0 ? => s21(BREQ);
0 1 0 => s22(BREQ BGACK); -- start, lword aligned
0 1 1 => s30(BREQ BGACK); -- start, lword unaligned (can only happen very first time)
endcase => ANY;

-- Attempt to read the entire aligned longword

s22: goto s23(BGACK PAS PDS PLHW PLLW DIEL DIEH); -- start CPU cycle (lword aligned)

s23: if NOT STERM_ then s35(BGACK INCFIFO DIEL DIEH) -- first cycle of a CPU access
else s24(BGACK PAS PDS PLHW PLLW DIEL DIEH);

s24: case (STERM_ DSACK DSACK1_ DSACK0_) -- wait for cycle to end
0 0 ? ? => s35(BGACK INCFIFO DIEL DIEH); -- 32 bit cycle terminated
1 0 ? ? => s24(BGACK PAS PDS PLHW PLLW DIEL DIEH); -- Cycle not yet terminated
? 1 0 0 => s35(BGACK INCFIFO DIEL DIEH); -- 32 bit cycle terminated
? 1 0 1 => s26(BGACK DIEH); -- 16 bit cycle terminated
endcase => ANY;

-- Responded as 16 bits only, so re-Read the 16 bit oddword value from this 16 bit port

s26: goto s27(BGACK PAS PDS PLLW SIZE1 DIEH BRIDGEIN); -- start CPU cycle (lword unaligned)

s27: goto s28(BGACK PAS PDS PLLW SIZE1 DIEH BRIDGEIN); -- No need to check STERM 'cause if it was a 32 bit port we never
-- could have got here...

s28: if NOT DSACK then s28(BGACK PAS PDS PLLW SIZE1 DIEH BRIDGEIN) -- Cycle not yet terminated
else s35(BGACK SIZE1 DIEH BRIDGEIN INCFIFO); -- cycle terminated (can assume it was via 16 bit DSACK)

-- Special case for very first odd word access of a DMA. We don't know which half of the data bus will have the
-- data. If 32 bit port then word will be on D0-D15. If 16 bit port then word will be on D16-D31. Assume it is
-- a 32 bit port. If it responds as a 16 bit port, then we have to re-direct the latched input data on D16-D31 to
-- D0-D15 and then latch it into the FIFO. This adds a couple of extra cycles before *AS is asserted to begin the
-- next DMA read. However, this only happens at worst case 1 time for an entire DMA.

s30: goto s31(BGACK PAS PDS PLLW SIZE1 DIEH DIEL); -- start CPU cycle (lword unaligned)

s31: if NOT STERM_ then s35(BGACK SIZE1 DIEH DIEL INCFIFO) -- first cycle of a CPU access
else s32(BGACK PAS PDS PLLW SIZE1 DIEH DIEL);

s32: case (STERM_ DSACK DSACK1_ DSACK0_) -- wait for cycle to end
0 0 ? ? => s35(BGACK SIZE1 DIEH DIEL INCFIFO); -- 32 bit cycle terminated
1 0 ? ? => s32(BGACK PAS PDS PLLW SIZE1 DIEH DIEL); -- Cycle not yet terminated
? 1 0 0 => s35(BGACK SIZE1 DIEH DIEL INCFIFO); -- 32 bit cycle terminated
? 1 0 1 => s33(BGACK PLLW SIZE1); -- 16 bit cycle terminated, so gotta bridge & such...
endcase => ANY;

s33: goto s34(BGACK PLLW BRIDGEIN);

s34: goto s35(BGACK BRIDGEIN INCFIFO);

-- Check if there is any more room in the FIFO for more data

s35: case (FIFOFULL) -- check if room in FIFO for more
1 => letgo(BGACK INCNI); -- no more room in FIFO
0 => s23(BGACK PAS PDS PLHW PLLW DIEL DIEH INCNI); -- start another CPU cycle
endcase => ANY;

letgo: goto s0;
`

@MHeinrichs
Copy link
Author

Or as a zip file with better formating (especially for the ASCII-waveforms):

SDMAC.zip

@mbtaylor1982
Copy link
Owner

thanks so much @MHeinrichs this will really help in validating the state machines

@MHeinrichs
Copy link
Author

You are very welcome

@mbtaylor1982
Copy link
Owner

I had a look at the file over lunch and it appears that the current implementation of the SCSI FSM is pretty Damn close to what is in the file already. I'm also so glad to see that the comments make sense and align with my current understanding for both the FSMs

@mbtaylor1982 mbtaylor1982 added the documentation Improvements or additions to documentation label Oct 6, 2024
@mbtaylor1982
Copy link
Owner

mbtaylor1982 commented Oct 8, 2024

these docs where used to reimplement the state machines in release v0.4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

2 participants