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

Subversion Repositories wb_lpc

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

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

//////////////////////////////////////////////////////////////////////
////                                                              ////
////  $Id: wb_lpc_host.v,v 1.4 2008-07-26 19:15:31 hharte Exp $   ////
////  wb_lpc_host.v - Wishbone Slave to LPC Host 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_host(clk_i, nrst_i, wbs_adr_i, wbs_dat_o, wbs_dat_i, wbs_sel_i, wbs_tga_i, wbs_we_i,
                   wbs_stb_i, wbs_cyc_i, wbs_ack_o, wbs_err_o,
                   dma_chan_i, dma_tc_i,
                   lframe_o, lad_i, lad_o, lad_oe
);
    // Wishbone Slave Interface
    input              clk_i;
    input              nrst_i;             // Active low reset.
    input       [31:0] wbs_adr_i;
    output      [31:0] wbs_dat_o;
    input       [31:0] wbs_dat_i;
    input       [3:0]  wbs_sel_i;
    input       [1:0]  wbs_tga_i;
    input              wbs_we_i;
    input              wbs_stb_i;
    input              wbs_cyc_i;
    output reg         wbs_ack_o;
    output reg         wbs_err_o;
 
    // LPC Master Interface
    output reg        lframe_o;     // LPC Frame output (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
    input       [2:0] dma_chan_i;   // DMA Channel
    input             dma_tc_i;     // DMA Terminal Count
 
    reg         [13:0] state;       // Current state
    reg         [2:0] adr_cnt;      // Address nibbe counter
    reg         [3:0] dat_cnt;      // Data nibble counter
    reg         [2:0] xfr_len;      // Number of nibbls for transfer
    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 input word.
 
    //
    // generate wishbone register signals
    wire wbs_acc = wbs_cyc_i & wbs_stb_i;    // Wishbone access
    wire wbs_wr  = wbs_acc & wbs_we_i;       // Wishbone write access
 
    // Memory Cycle (tga== 1'b00) is bit 2=1 for LPC Cycle Type.
    wire    mem_xfr = (wbs_tga_i == `WB_TGA_MEM);
    wire    dma_xfr = (wbs_tga_i == `WB_TGA_DMA);
    wire    fw_xfr  = (wbs_tga_i == `WB_TGA_FW);
 
    assign wbs_dat_o[31:0] = lpc_dat_i; 
 
    always @(posedge clk_i or negedge nrst_i)
        if(~nrst_i)
        begin
            state <= `LPC_ST_IDLE;
            lframe_o <= 1'b0;
            wbs_ack_o <= 1'b0;
            wbs_err_o <= 1'b0;
            lad_oe <= 1'b0;
            lad_o <= 4'b0;
            adr_cnt <= 3'b0;
            dat_cnt <= 4'h0;
            xfr_len <= 3'b000;
            lpc_dat_i <= 32'h00000000;
        end
        else begin
            case(state)
                `LPC_ST_IDLE:
                    begin
                        wbs_ack_o <= 1'b0;
                        wbs_err_o <= 1'b0;
                        lframe_o <= 1'b0;
                        dat_cnt <= 4'h0;                        
 
                        if(wbs_acc)     // Wishbone access starts LPC transaction
                            state <= `LPC_ST_START;
                        else
                            state <= `LPC_ST_IDLE;
                    end
                `LPC_ST_START:
                    begin
                        lframe_o <= 1'b1;
                        if(~fw_xfr) begin       // For Memory and I/O Cycles
                            lad_o   <= `LPC_START;
                            state   <= `LPC_ST_CYCTYP;
                        end
                        else begin              // Firmware Read and Write Cycles
                            if(wbs_wr)
                                lad_o <= `LPC_FW_WRITE;
                            else
                                lad_o <= `LPC_FW_READ;
 
                            state   <= `LPC_ST_ADDR;
                        end
                        lad_oe  <= 1'b1;
                        adr_cnt <= ((mem_xfr | fw_xfr) ? 3'b000 : 3'b100);
                    end
                `LPC_ST_CYCTYP:
                    begin
                        lframe_o    <= 1'b0;
                        lad_oe  <= 1'b1;                
 
                        if(~dma_xfr)
                            begin
                                lad_o   <= {1'b0,mem_xfr,wbs_wr,1'b0};      // Cycle Type/Direction for I/O or MEM
                                state       <= `LPC_ST_ADDR;
                            end
                        else // it is DMA
                            begin
                                lad_o   <= {1'b1,1'b0,~wbs_wr,1'b0};        // Cycle Type/Direction for DMA, r/w is inverted for DMA
                                state       <= `LPC_ST_CHAN;
                            end
                    end
                `LPC_ST_ADDR:   // Output four nubbles of address.
                    begin
                        lframe_o <= 1'b0;       // In case we came here from a Firmware cycle, which skips CYCTYP.
 
                        // The LPC Bus Address is sent across the bus a nibble at a time;
                        // however, the most significant nibble is sent first.  For firmware and
                        // memory cycles, the address is 32-bits.  Actually, for firmware accesses,
                        // the most significant nibble is known as the IDSEL field.  For I/O,
                        // the address is only 16-bits wide.
                        case(adr_cnt)
                            3'h0:
                                lad_o <= wbs_adr_i[31:28];
                            3'h1:
                                lad_o <= wbs_adr_i[27:24];
                            3'h2:
                                lad_o <= wbs_adr_i[23:20];
                            3'h3:
                                lad_o <= wbs_adr_i[19:16];
                            3'h4:
                                lad_o <= wbs_adr_i[15:12];
                            3'h5:
                                lad_o <= wbs_adr_i[11:8];
                            3'h6:
                                lad_o <= wbs_adr_i[7:4];
                            3'h7:
                                lad_o <= wbs_adr_i[3:0];
                        endcase
 
                        adr_cnt <= adr_cnt + 1;
 
                        if(adr_cnt == 4'h7) // Last address nibble.
                            begin
                                if(~fw_xfr)
                                    if(wbs_wr)
                                        state <= `LPC_ST_H_DATA;
                                    else
                                        state <= `LPC_ST_H_TAR1;
                                else    // For firmware read/write, we need to transfer the MSIZE nibble
                                    state <= `LPC_ST_SIZE;
                            end
                        else
                            state <= `LPC_ST_ADDR;
 
                        lad_oe  <= 1'b1;
                        xfr_len     <= 3'b001;      // One Byte Transfer
                    end
                `LPC_ST_CHAN:
                    begin
                        lad_o   <= {dma_tc_i, dma_chan_i};
                        state <= `LPC_ST_SIZE;
                    end
                `LPC_ST_SIZE:
                    begin
                        case(wbs_sel_i)
                            `WB_SEL_BYTE:
                                begin
                                    xfr_len <= 3'b001;
                                    lad_o <= 4'h0;
                                end
                            `WB_SEL_SHORT:
                                begin
                                    xfr_len <= 3'b010;
                                    lad_o <= 4'h1;
                                end
                            `WB_SEL_WORD:
                                begin
                                    xfr_len <= 3'b100;
                                    if(fw_xfr)              // Firmware transfer uses '2' for 4-byte transfer.
                                        lad_o <= 4'h2;
                                    else                    // DMA uses '3' for 4-byte transfer.
                                        lad_o <= 4'h3;
                                end
                            default:
                                begin
                                    xfr_len <= 3'b001;
                                    lad_o <= 4'h0;
                                end
                        endcase
                        if(wbs_wr)
                            state <= `LPC_ST_H_DATA;
                        else
                            state <= `LPC_ST_H_TAR1;
                    end
 
                `LPC_ST_H_DATA:
                    begin
                        lad_oe  <= 1'b1;
                        case(dat_cnt)   // We only support a single byte for I/O.
                            4'h0:
                                lad_o <= wbs_dat_i[3:0];
                            4'h1:
                                lad_o <= wbs_dat_i[7:4];
                            4'h2:
                                lad_o <= wbs_dat_i[11:8];
                            4'h3:
                                lad_o <= wbs_dat_i[15:12];
                            4'h4:
                                lad_o <= wbs_dat_i[19:16];
                            4'h5:
                                lad_o <= wbs_dat_i[23:20];
                            4'h6:
                                lad_o <= wbs_dat_i[27:24];
                            4'h7:
                                lad_o <= wbs_dat_i[31:28];
                            default:
                                lad_o <= 4'hx;
                        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
                        lad_o <= 4'b1111;       // Drive LAD high
                        lad_oe <= 1'b1;
                        state <= `LPC_ST_H_TAR2;
                    end
                `LPC_ST_H_TAR2:
                    begin
                        lad_oe <= 1'b0;     // float LAD
                        state <= `LPC_ST_SYNC;
                    end
                `LPC_ST_SYNC:
                    begin
                        lad_oe <= 1'b0;     // float LAD
                        if((lad_i == `LPC_SYNC_READY) || (lad_i == `LPC_SYNC_MORE)) begin
                            if(wbs_wr) begin
                                state <= `LPC_ST_P_TAR1;
                            end
                            else begin
                                state <= `LPC_ST_P_DATA;
                            end
                        end else if(lad_i == `LPC_SYNC_ERROR) begin
                            dat_cnt <= { xfr_len, 1'b1 };    // Terminate data transfer
                            wbs_err_o <= 1'b1;    // signal wishbone error
                            state <= `LPC_ST_P_TAR1;
                        end else begin
                            state <= `LPC_ST_SYNC;
                        end
                    end
 
                `LPC_ST_P_DATA:
                    begin
                        case(dat_cnt)
                            4'h0:
                                lpc_dat_i[3:0] <= lad_i;
                            4'h1:
                                lpc_dat_i[7:4] <= lad_i;
                            4'h2:
                                lpc_dat_i[11:8] <= lad_i;
                            4'h3:
                                lpc_dat_i[15:12] <= lad_i;
                            4'h4:
                                lpc_dat_i[19:16] <= lad_i;
                            4'h5:
                                lpc_dat_i[23:20] <= lad_i;
                            4'h6:
                                lpc_dat_i[27:24] <= lad_i;
                            4'h7:
                                lpc_dat_i[31:28] <= lad_i;
                        endcase
 
                        dat_cnt <= dat_cnt + 1;
 
                        if(nibble_cnt == 1'b1)          // Byte transfer complete
                            if (byte_cnt == xfr_len-1)  // End of data transfer phase
                                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                            // Go to next nibble
                            state <= `LPC_ST_P_DATA;
                    end
                `LPC_ST_P_TAR1:
                    begin
                        lad_oe <= 1'b0;
                        if(byte_cnt == xfr_len) begin
                            state <= `LPC_ST_WB_RETIRE;
                            wbs_ack_o <= wbs_acc;
                        end
                        else begin
                            if(wbs_wr) begin    // DMA READ (Host to Peripheral)
                                state <= `LPC_ST_H_DATA;
                            end
                            else begin  // unhandled READ case
                                state <= `LPC_ST_IDLE;
                            end
                        end
                    end
                `LPC_ST_WB_RETIRE:
                    begin
                        wbs_ack_o <= 1'b0;
                        wbs_err_o <= 1'b0;
                        if(wbs_acc) begin
                            state <= `LPC_ST_WB_RETIRE;
                        end
                        else begin
                            state <= `LPC_ST_IDLE;
                        end
                    end
            endcase
        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.