module BusControl (CLK, RST, NewCmd, Com, Busy, SHAddr, SHData, INCAddr, Addr15, ReadMem, ReadReg, WriteMem, WriteReg, LDReadData, SHReadData, EnReadDataL, XBusy) ; input CLK ; input RST; input NewCmd ; // Asserted when a new XBUS command has arrived input [1:0] Com ; // XBUS command input Busy ; // Busy signal from Memory input Addr15 ; // High order bit of address output SHAddr ; // Shift next nibble into address register output SHData ; // Shift next nibble into data register output INCAddr ; // Increment the address register output ReadMem ; // Generate a read signal for memory space output ReadReg ; // Read signal for register space output WriteMem ; // Generate a write signal for memory space output WriteReg ; // Write signal for register space output LDReadData ; // Load Read Data into register output SHReadData ; // Select high byte of Read data output EnReadDataL ; // Drive data bus with Read data (assert low) output XBusy ; // Tell XBUS that we are busy reg SHAddr ; reg SHData ; reg INCAddr ; reg Read ; reg Write ; reg LDReadData ; reg SHReadData ; reg XBusy ; // We are busy only if we are addressing memory wire MemBusy = Busy && !Addr15; // Memory space is the low 32K of the address space assign ReadMem = Read && !Addr15; assign ReadReg = Read && Addr15; assign WriteMem = Write && !Addr15; assign WriteReg = Write && Addr15; // FSM states parameter IDLE = 0, ADDR1 = 1, ADDR2 = 2, ADDR3 = 3, DATA0 = 4, RDDATA0 = 7, RDDATA1 = 5, WRDATA1 = 6, WRITING = 8, WAITREAD = 9, READDONE = 10, WAITWRITE = 11, WAITWRITE2 = 12, WAITREAD2 = 13, WAITREAD3 = 14, HOLDREAD0 = 15; // XBUS commands parameter READ = 3, WRITE = 2, ADDR = 1; reg [3:0] state, nextState; always @(posedge CLK) begin if (RST) begin state <= IDLE; end else begin state <= nextState; end end assign EnReadDataL = !(Com==READ); always @(state or NewCmd or Com or MemBusy) begin nextState = state; // Stay in the same state by default SHAddr = 0; // Control signals are inactive by default SHData = 0; Read = 0; Write = 0; INCAddr = 0; XBusy = 0; SHReadData = 0; // Send low order nibble first LDReadData = 0; case (state) // synopsys parallel_case IDLE: // This is the IDLE state begin if (NewCmd) begin case (Com) // synopsys parallel_case ADDR: begin SHAddr = 1; nextState = ADDR1; end WRITE: begin INCAddr = 1; // Autoincrement since no address sent SHData = 1; // Latch first data nibble nextState = WRDATA1; end READ: // If we get another read, autoincrement begin INCAddr = 1; // Autoincrement since no address sent nextState = WAITREAD; // Address ready on next cycle end default: nextState = IDLE; endcase end end ADDR1: // Second address cycle (assume Com = ADDR) if (NewCmd) begin if (Com == ADDR) begin SHAddr = 1; nextState = ADDR2; end else nextState = IDLE; // Go to idle on error end ADDR2: // Third address cycle if (NewCmd) begin if (Com == ADDR) begin SHAddr = 1; nextState = ADDR3; end else nextState = IDLE; // Go to idle on error end ADDR3: // Fourth address cycle if (NewCmd) begin if (Com == ADDR) begin SHAddr = 1; nextState = DATA0; end else nextState = IDLE; // Go to idle on error end DATA0: // First data cycle - we find out here if read or write if (NewCmd) begin if (Com == WRITE) begin SHData = 1; nextState = WRDATA1; end else if (Com == READ) begin Read = 1; // Read from memory if (MemBusy) begin // Hold up the XBUS if we are busy XBusy = 1; nextState = WAITREAD; end else nextState = READDONE; end else nextState = IDLE; // Go to idle on error end WRDATA1: // Second data write cycle if (NewCmd) begin if (Com == WRITE) begin SHData = 1; nextState = WRITING; end else nextState = IDLE; // Go to IDLE on error end WRITING: // Perform the write now that we have the data begin Write = 1; // Request a Write if (MemBusy) begin XBusy = 1; // Keep requesting until MemBusy goes away nextState = WAITWRITE; end else nextState = IDLE; // Go to IDLE when done end WAITWRITE: // Wait for write to complete begin Write = 1; if (MemBusy) begin XBusy = 1; // Keep requesting until MemBusy goes away end else nextState = WAITWRITE2; // Now wait for extra NewCmd end WAITWRITE2: // XBusy causes an extra NewCmd - discard before proceeding if (NewCmd) nextState = IDLE; WAITREAD: // Request a read until MemBusy goes away begin Read = 1; if (MemBusy) begin XBusy = 1; // Hold up XBUS until we are done end else nextState = WAITREAD2; end WAITREAD2: begin LDReadData = 1; // Memory read is pipelined - data ready here nextState = WAITREAD3; end WAITREAD3: // Wait for the extra NewCmd caused by XBusy if (NewCmd) begin nextState = HOLDREAD0; end READDONE: // Read returns right away begin LDReadData = 1; // Memory read is pipelined - data ready here nextState = HOLDREAD0; end HOLDREAD0: // Hold low nibble while waiting for NewCmd begin if (NewCmd) begin if (Com == READ) begin SHReadData = 1; // Now put out high nibble nextState = IDLE; end else nextState = IDLE; // Go to IDLE on error end end endcase end endmodule