OpenCores
URL https://opencores.org/ocsvn/wb_lpc/wb_lpc/trunk

Subversion Repositories wb_lpc

[/] [wb_lpc/] [trunk/] [rtl/] [verilog/] [wb_lpc_periph.v] - Rev 22

Go to most recent revision | Compare with Previous | Blame | View Log

//////////////////////////////////////////////////////////////////////
////                                                              ////
////  $Id: wb_lpc_periph.v,v 1.4 2008-07-26 19:15:32 hharte Exp $ ////
////  wb_lpc_periph.v - LPC Peripheral to Wishbone Master Bridge  ////
////                                                              ////
////  This file is part of the Wishbone LPC Bridge project        ////
////  http://www.opencores.org/projects/wb_lpc/                   ////
////                                                              ////
////  Author:                                                     ////
////      - Howard M. Harte (hharte@opencores.org)                ////
////                                                              ////
//////////////////////////////////////////////////////////////////////
////                                                              ////
//// Copyright (C) 2008 Howard M. Harte                           ////
////                                                              ////
//// This source file may be used and distributed without         ////
//// restriction provided that this copyright statement is not    ////
//// removed from the file and that any derivative work contains  ////
//// the original copyright notice and the associated disclaimer. ////
////                                                              ////
//// This source file is free software; you can redistribute it   ////
//// and/or modify it under the terms of the GNU Lesser General   ////
//// Public License as published by the Free Software Foundation; ////
//// either version 2.1 of the License, or (at your option) any   ////
//// later version.                                               ////
////                                                              ////
//// This source is distributed in the hope that it will be       ////
//// useful, but WITHOUT ANY WARRANTY; without even the implied   ////
//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      ////
//// PURPOSE.  See the GNU Lesser General Public License for more ////
//// details.                                                     ////
////                                                              ////
//// You should have received a copy of the GNU Lesser General    ////
//// Public License along with this source; if not, download it   ////
//// from http://www.opencores.org/lgpl.shtml                     ////
////                                                              ////
//////////////////////////////////////////////////////////////////////
 
`timescale 1 ns / 1 ns
 
`include "../../rtl/verilog/wb_lpc_defines.v"
 
//              I/O Write       I/O Read        DMA Read        DMA Write
//                                                          
//  States - 1. H Start         H Start         H Start         H Start
//           2. H CYCTYPE+DIR   H CYCTYPE+DIR   H CYCTYPE+DIR   H CYCTYPE+DIR
//           3. H Addr (4)      H Addr (4)      H CHAN+TC       H CHAN+TC
//                                              H SIZE          H SIZE
//           4. H Data (2)      H TAR  (2)    +-H DATA (2)      H TAR  (2)
//           5. H TAR  (2)      P SYNC (1+)   | H TAR  (2)    +-P SYNC (1+)
//           6. P SYNC (1+)     P DATA (2)    | H SYNC (1+)   +-P DATA (2)
//           7. P TAR  (2)      P TAR  (2)    +-P TAR  (2)      P TAR
//                                                          
 
module wb_lpc_periph(clk_i, nrst_i, wbm_adr_o, wbm_dat_o, wbm_dat_i, wbm_sel_o, wbm_tga_o, wbm_we_o,
                     wbm_stb_o, wbm_cyc_o, wbm_ack_i, wbm_err_i,
                     dma_chan_o, dma_tc_o,
                     lframe_i, lad_i, lad_o, lad_oe
);
 
    // Wishbone Master Interface
    input              clk_i;
    input              nrst_i;
    output reg  [31:0] wbm_adr_o;
    output reg  [31:0] wbm_dat_o;
    input       [31:0] wbm_dat_i;
    output reg   [3:0] wbm_sel_o;
    output reg   [1:0] wbm_tga_o;
    output reg         wbm_we_o;
    output reg         wbm_stb_o;
    output reg         wbm_cyc_o;
    input              wbm_ack_i;
    input              wbm_err_i;	 
 
    // LPC Slave Interface
    input              lframe_i;    // LPC Frame input (active high)
    output reg         lad_oe;      // LPC AD Output Enable
    input        [3:0] lad_i;       // LPC AD Input Bus
    output reg   [3:0] lad_o;       // LPC AD Output Bus
 
    // DMA-Specific sideband signals
    output       [2:0] dma_chan_o;  // DMA Channel
    output             dma_tc_o;    // DMA Terminal Count
 
    reg         [13:0] state;       // Current state
    reg          [2:0] adr_cnt;     // Address nibble counter
    reg          [3:0] dat_cnt;     // Data nibble counter
    wire         [2:0] byte_cnt = dat_cnt[3:1];  // Byte counter
    wire               nibble_cnt = dat_cnt[0];  // Nibble counter
 
    reg         [31:0] lpc_dat_i;   // Temporary storage for LPC input data.
    reg                mem_xfr;     // LPC Memory Transfer (not I/O)
    reg                dma_xfr;     // LPC DMA Transfer
    reg                fw_xfr;      // LPC Firmware memory read/write
    reg          [2:0] xfr_len;     // Number of nibbls for transfer
    reg                dma_tc;      // DMA Terminal Count
    reg          [2:0] dma_chan;    // DMA Channel
 
    // These buffer enough state to delay the start of the next Wishbone cycle
    // until the previous Firmware Write has completed.
    reg         [31:0] lpc_adr_reg; // Temporary storage for address received on LPC bus.
    reg         [31:0] lpc_dat_o;   // Temporary storage for LPC output data.
    reg                lpc_write;   // Holds current LPC transfer direction
    reg          [1:0] lpc_tga_o;
    reg                got_ack;     // Set when ack has been received from wbm
 
    assign dma_chan_o = dma_chan;
    assign dma_tc_o = dma_tc;
 
    always @(posedge clk_i or negedge nrst_i)
    begin
        if(~nrst_i)
        begin
            state <= `LPC_ST_IDLE;
            lpc_adr_reg <= 32'h00000000;
            lpc_dat_o <= 32'h00000000;
            lpc_write <= 1'b0;
            lpc_tga_o <= `WB_TGA_MEM;
            lad_oe <= 1'b0;
            lad_o <= 8'hFF;
            lpc_dat_i <= 32'h00000000;
            mem_xfr <= 1'b0;
            dma_xfr <= 1'b0;
            fw_xfr <= 1'b0;
            xfr_len <= 3'b000;
            dma_tc <= 1'b0;
            dma_chan <= 3'b000;
        end
        else begin
            case(state)
                `LPC_ST_IDLE:
                    begin
                        dat_cnt <= 4'h0;
                        if(lframe_i) begin
                            lad_oe <= 1'b0;
                            xfr_len <= 3'b001;
 
                            if(lad_i == `LPC_START) begin
                                state <= `LPC_ST_CYCTYP;
                                lpc_write <= 1'b0;
                                fw_xfr <= 1'b0;                                 
                            end
                            else if ((lad_i == `LPC_FW_WRITE) || (lad_i == `LPC_FW_READ)) begin
                                state <= `LPC_ST_ADDR;
                                lpc_write <= (lad_i == `LPC_FW_WRITE) ? 1'b1 : 1'b0;
                                adr_cnt <= 3'b000;
                                fw_xfr <= 1'b1;
                                dma_xfr <= 1'b0;
                                lpc_tga_o <= `WB_TGA_FW;
                            end
                            else
                                state <= `LPC_ST_IDLE;
                        end
                        else
                            state <= `LPC_ST_IDLE;
                    end
                `LPC_ST_CYCTYP:
                    begin
                        lpc_write <= (lad_i[3] ? ~lad_i[1] : lad_i[1]);  // Invert we_o if we are doing DMA.
                        adr_cnt <= (lad_i[2] ? 3'b000 : 3'b100);
                        if(lad_i[3]) begin // dma_xfr
                            lpc_tga_o <= `WB_TGA_DMA;
                            dma_xfr <= 1'b1;
                            mem_xfr <= 1'b0;
                            state <= `LPC_ST_CHAN;									 
                        end
                        else if(lad_i[2]) begin // mem_xfr
                            lpc_tga_o <= `WB_TGA_MEM;
                            dma_xfr <= 1'b0;
                            mem_xfr <= 1'b1;
                            state <= `LPC_ST_ADDR;
                        end
                        else begin
                            lpc_tga_o <= `WB_TGA_IO;
                            dma_xfr <= 1'b0;
                            mem_xfr <= 1'b0;
                            state <= `LPC_ST_ADDR;
                        end
                    end
                `LPC_ST_ADDR:
                    begin
                        case(adr_cnt)
                            3'h0: lpc_adr_reg[31:28] <= lad_i;
                            3'h1: lpc_adr_reg[27:24] <= lad_i;
                            3'h2: lpc_adr_reg[23:20] <= lad_i;
                            3'h3: lpc_adr_reg[19:16] <= lad_i;
                            3'h4: lpc_adr_reg[15:12] <= lad_i;
                            3'h5: lpc_adr_reg[11: 8] <= lad_i;
                            3'h6: lpc_adr_reg[ 7: 4] <= lad_i;
                            3'h7: lpc_adr_reg[ 3: 0] <= lad_i;
                        endcase
 
                        adr_cnt <= adr_cnt + 1;
 
                        if(adr_cnt == 3'h7) // Last address nibble.
                            begin
                                if(~fw_xfr)
                                    if(lpc_write)
                                        state <= `LPC_ST_H_DATA;
                                    else
                                        state <= `LPC_ST_H_TAR1;
                                else    // For firmware read/write, we need to read the MSIZE nibble
                                    state <= `LPC_ST_SIZE;
                            end
                        else
                            state <= `LPC_ST_ADDR;
                    end
                `LPC_ST_CHAN:
                    begin
                        lpc_adr_reg <= 32'h00000000;      // Address lines not used for DMA.
                        dma_tc <= lad_i[3];
                        dma_chan <= lad_i[2:0];
                        state <= `LPC_ST_SIZE;
                    end
                `LPC_ST_SIZE:
                    begin
                        case(lad_i)
                            4'h0:    xfr_len <= 3'b001;
                            4'h1:    xfr_len <= 3'b010;
                            4'h2:    xfr_len <= 3'b100;   // Firmware transfer uses '2' for 4-byte transfer.
                            4'h3:    xfr_len <= 3'b100;   // DMA uses '3' for 4-byte transfer.
                            default: xfr_len <= 3'b001;
                        endcase
                        if(lpc_write)
                            state <= `LPC_ST_H_DATA;
                        else
                            state <= `LPC_ST_H_TAR1;
                    end
                `LPC_ST_H_DATA:
                    begin
                        case(dat_cnt)
                            4'h0: lpc_dat_o[ 3: 0] <= lad_i;
                            4'h1: lpc_dat_o[ 7: 4] <= lad_i;
                            4'h2: lpc_dat_o[11: 8] <= lad_i;
                            4'h3: lpc_dat_o[15:12] <= lad_i;
                            4'h4: lpc_dat_o[19:16] <= lad_i;
                            4'h5: lpc_dat_o[23:20] <= lad_i;
                            4'h6: lpc_dat_o[27:24] <= lad_i;
                            4'h7: lpc_dat_o[31:28] <= lad_i;
                        endcase
 
                        dat_cnt <= dat_cnt + 1;
 
                        if(nibble_cnt == 1'b1) // end of byte
                            begin
                                if((fw_xfr) && (byte_cnt != xfr_len-1)) // Firmware transfer does not have TAR between bytes.
                                    state <= `LPC_ST_H_DATA;
										  else
                                    state <= `LPC_ST_H_TAR1;
                            end
                        else
                            state <= `LPC_ST_H_DATA;
 
                    end
 
                `LPC_ST_H_TAR1:
                    begin
                        // It is ok to start the Wishbone Cycle, done below...
                        state <= `LPC_ST_H_TAR2;
                    end
                `LPC_ST_H_TAR2:
                    begin
                        state <= (fw_xfr & lpc_write) ? `LPC_ST_FWW_SYNC : `LPC_ST_SYNC;
                        lad_o <= (fw_xfr & lpc_write) ? `LPC_SYNC_READY : `LPC_SYNC_SWAIT;
                        lad_oe <= 1'b1;     // start driving LAD
                    end
                `LPC_ST_SYNC:
                    begin
                        lad_oe <= 1'b1;     // start driving LAD
                        // First byte of WB read, last byte of WB write
                        if(((byte_cnt == xfr_len) & lpc_write) | ((byte_cnt == 0) & ~lpc_write)) begin
                            // Errors can not be signalled for Firmware Memory accesses according to the spec.
                            if((wbm_err_i) && (~fw_xfr)) begin
                                dat_cnt <= { xfr_len, 1'b1 }; // Abort remainder of transfer
                                lad_o <= `LPC_SYNC_ERROR;   // Bus error
                                state <= `LPC_ST_P_TAR1;
                            end else if(got_ack) begin
                                if(lpc_write) begin
                                    lad_o <= `LPC_SYNC_READY;   // Ready
                                    state <= `LPC_ST_P_TAR1;
                                end
                                else begin
                                    // READY+MORE for multi-byte DMA, except the final byte.
                                    // For non-DMA cycles, only READY
                                    lad_o <= (((xfr_len == 1) & ~lpc_write) || (~dma_xfr)) ? `LPC_SYNC_READY : `LPC_SYNC_MORE;
                                    state <= `LPC_ST_P_DATA;
                                end
                            end
                            else begin
                                state <= `LPC_ST_SYNC;
                                lad_o <= `LPC_SYNC_SWAIT;
                            end
                        end
                        else begin  // Multi-byte transfer, just ack right away.
                            if(lpc_write) begin
                                lad_o <= (dma_xfr) ? `LPC_SYNC_MORE : `LPC_SYNC_READY;
                                state <= `LPC_ST_P_TAR1;
                            end
									 else begin
                                lad_o <= ((byte_cnt == xfr_len-1) || (~dma_xfr)) ? `LPC_SYNC_READY : `LPC_SYNC_MORE;   // Ready-More									 
                                state <= `LPC_ST_P_DATA;
                            end
                        end
                    end
                `LPC_ST_FWW_SYNC:	// Firmware write requires a special SYNC without wait-states.
                    begin
                        lad_o <= 4'hF;
                        state <= `LPC_ST_P_TAR2;
                    end
 
                `LPC_ST_P_DATA:
                    begin
                        case(dat_cnt)
                            4'h0: lad_o <= lpc_dat_i[ 3: 0];
                            4'h1: lad_o <= lpc_dat_i[ 7: 4];
                            4'h2: lad_o <= lpc_dat_i[11: 8];
                            4'h3: lad_o <= lpc_dat_i[15:12];
                            4'h4: lad_o <= lpc_dat_i[19:16];
                            4'h5: lad_o <= lpc_dat_i[23:20];
                            4'h6: lad_o <= lpc_dat_i[27:24];
                            4'h7: lad_o <= lpc_dat_i[31:28];
                        endcase
 
                        dat_cnt <= dat_cnt + 1;
 
                        if(nibble_cnt == 1'b1)  // Byte transfer complete
                            if (byte_cnt == xfr_len-1) // Byte transfer complete
                                state <= `LPC_ST_P_TAR1;
                            else begin
                                if(fw_xfr) // Firmware transfer does not have TAR between bytes.
                                    state <= `LPC_ST_P_DATA;
                                else
                                    state <= `LPC_ST_SYNC;
                            end
                        else
                            state <= `LPC_ST_P_DATA;
 
                        lad_oe <= 1'b1;
                    end
                `LPC_ST_P_TAR1:
                    begin
                        lad_oe <= 1'b1;
                        lad_o <= 4'hF;
                        state <= `LPC_ST_P_TAR2;
                    end
                `LPC_ST_P_TAR2:
                    begin
                        lad_oe <= 1'b0;     // float LAD
                        if(byte_cnt == xfr_len) begin
                            state <= `LPC_ST_IDLE;
                        end
                        else begin
                            if(lpc_write) begin  // DMA READ (Host to Peripheral)
                                state <= `LPC_ST_P_WAIT1;
                            end
                            else begin  // unhandled READ case
                                state <= `LPC_ST_IDLE;
                            end
                        end
 
                    end
                    `LPC_ST_P_WAIT1:
                         state <= `LPC_ST_H_DATA;
            endcase
        end
 
// The goal here is to split the Wishbone cycle handling out of the main state-machine
// so it can run independently.  This is needed so that in the case of a firmware write,
// where the FLASH requires wait-states (which are not allowed for FW write according to
// the LPC Specification.)  In this case, since the FLASH cannot insert wait-states,
// a subsequent LPC operation (which must not be another FW Write) will insert wait-
// states before starting the next Wishbone master cycle.
//
// The only reason that I can think of for the LPC spec to mandate that Firmware Writes
// must not insert wait-states is that since FLASH writes can take a very long time,
// the LPC spec disallowed them to force LPC FLASH programming software to use polling
// to determine when the write is complete rather than inserting a bunch of wait-states,
// which would use up too much LPC bus bandwidth, and block other requests from getting
// through.
//
        if(~nrst_i)
        begin
            wbm_adr_o <= 32'h00000000;
            wbm_dat_o <= 32'h00000000;
            wbm_stb_o <= 1'b0;
            wbm_cyc_o <= 1'b0;
            wbm_we_o <= 1'b0;
            wbm_sel_o <= 4'b0000;
            wbm_tga_o <= `WB_TGA_MEM;
            got_ack <= 1'b0;
        end
        else begin
            if ((state == `LPC_ST_H_TAR1) && (((byte_cnt == xfr_len) & lpc_write) | ((byte_cnt == 0) & ~lpc_write)))
            begin
                // Start Wishbone Cycle
                wbm_stb_o <= 1'b1;
                wbm_cyc_o <= 1'b1;
                wbm_adr_o <= lpc_adr_reg;
                wbm_dat_o <= lpc_dat_o;					 
                wbm_we_o <= lpc_write;
                wbm_tga_o <= lpc_tga_o;
                got_ack <= 1'b0;
                case(xfr_len)
                    3'h0: wbm_sel_o <= `WB_SEL_BYTE;
                    3'h2: wbm_sel_o <= `WB_SEL_SHORT;
                    3'h4: wbm_sel_o <= `WB_SEL_WORD;
                endcase
            end
            else if((wbm_stb_o == 1'b1) && (wbm_ack_i == 1'b1)) begin
                // End Wishbone Cycle
                wbm_stb_o <= 1'b0;
                wbm_cyc_o <= 1'b0;
                wbm_we_o <= 1'b0;
                got_ack <= 1'b1;
                if(~wbm_we_o) begin
                    lpc_dat_i <= wbm_dat_i;
                end
             end
        end
    end
endmodule
 
 
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.