📄 pci_memory.v
字号:
/* * Copyright 2002 Picture Elements * Stephen Williams <steve@icarus.com> * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU * General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */// $Id: pci_memory.v,v 1.16 2004/04/30 20:25:40 steve Exp $/* * The pci_memory device is a virtual memory device that acts like an * ordinary memory in the region of BAR0. There are magical control * registers in BAR1. * * BAR0 is the memory region. * * BAR1 is a command register set. It is 4K long, and used to carry * commands that the memory device itself can perform. Word-0 is the * command register, and writes to that address execute the specified * command. The remaining words are the arguments or results of the * command. * * The BAR1 commands are as follows: * * FILL: 0x00000000 <base> <size> <fill> * * The fill command causes the memory starting at <base> (the offset * within the memory region) and for <size> words to be filled with * the word value <fill> * * COMPARE: 0x00000001 <base1> <size1> <base2> * * The COMPARE command causes the memory to compare two regions of * memory, <base1> and <base2>. The <size1> is the size in bytes of * the regions. * * The result is as memcmp, and the result code replaces the command * word. * * LOAD: 0x00000002 <base> <size> <file name...> * * The LOAD command reads host file contents into the address at * <base> and for <size> bytes. The actual number of bytes loaded * is returned in the command word. If there is an error reading * the file, a -1 (0xffffffff) is returned instead. * * The file name is a null terminated string of bytes. The name * is interpreted by the host system. * * SAVE: 0x00000003 <base> <size> <file name...> * * The SAVE command writes a host file with the contents of memory * at offset <base> and for <size> bytes. * * The BAR2 region is registers of a DMA controller. The DMA controller * supports a signal linear read. * * 0x000 : Cmd/Status * [0] GO * [1] FIFO Mode (0 - linear external addresses, 1 - fixed address) * [2] Enable Count Interrupt * * 0x004 : External Address * * 0x008 : Memory Offset * * 0x00c : Transfer count */`timescale 1ns/1nsmodule pci_memory (CLK, RESET, AD, C_BE, PAR, FRAME, TRDY, IRDY, STOP, DEVSEL, IDSEL, REQ, GNT, INTA); // This is the device ID of this PCI device. parameter DEVICE_ID = 32'hffc0_12c5; // Use this to set the size of the memory region. parameter BAR0_MASK = 32'hffff0000; // Memories are normally prefetchable. parameter BAR0_FLAGS = 'h8; // This is the default memory image file. override this // parameter to use a different host file. parameter IMAGE = "memory.bin"; // This is the default retry rate to use. 0 means turn it off. parameter RETRY_RATE = 0; parameter DISCON_RATE = 0; localparam BAR1_MASK = 32'hfffff000; localparam BAR1_FLAGS = 'h8; // Use this to set the size of the DMA register region. localparam BAR2_MASK = 32'hfffff000; // registers are not prefetchable. localparam BAR2_FLAGS = 'h0; input CLK; input RESET; inout [31:0] AD; inout [3:0] C_BE; inout PAR; inout FRAME, TRDY, IRDY, STOP; inout DEVSEL; input IDSEL; output INTA; wire INTA = (dma_count == 0) && dma_count_intr_en; output REQ; input GNT; reg REQ = 1; reg PAR_reg; reg PAR_en; assign PAR = PAR_en? PAR_reg : 1'bz; reg FRAME_reg = 1; reg FRAME_en = 0; assign FRAME = FRAME_en? FRAME_reg : 1'bz; reg TRDY_reg; reg TRDY_en = 0; assign TRDY = TRDY_en? TRDY_reg : 1'bz; reg IRDY_reg = 1; reg IRDY_en = 0; assign IRDY = IRDY_en? IRDY_reg : 1'bz; reg STOP_reg; reg STOP_en = 0; assign STOP = STOP_en? STOP_reg : 1'bz; reg DEVSEL_reg; reg DEVSEL_en; assign DEVSEL = DEVSEL_en? DEVSEL_reg : 1'bz; reg [3:0] C_BE_reg; reg C_BE_en = 0; bufif1 c_be_buf[3:0](C_BE, C_BE_reg, C_BE_en); reg [31:0] AD_reg; reg AD_en = 0; bufif1 ad_buf[31:0](AD, AD_reg, AD_en); // This holds the configuration space of the device. Only the // first 16 words are implemented. reg [31:0] config_mem[15:0]; // Registers related to DMA (BAR2) reg dma_go = 0; reg dma_fifo = 1; reg dma_count_intr_en = 0; reg [31:0] dma_external; reg [31:0] dma_memory; reg [31:0] dma_count; // This function returns true if the input address addresses // this device. Use BAR0 and the BAR mask to decode the address. function [0:0] selected; input [31:0] addr; if ((config_mem[4] & BAR0_MASK) == 0) selected = 0; else if ((addr & BAR0_MASK) == (config_mem[4] & BAR0_MASK)) selected = 1; else selected = 0; endfunction // This checks if BAR1 has been selected. We handle different // things here. function [0:0] selected1; input [31:0] addr; if ((config_mem[5] & BAR1_MASK) == 0) selected1 = 0; else if ((addr & BAR1_MASK) == (config_mem[5] & BAR1_MASK)) selected1 = 1; else selected1 = 0; endfunction // This checks if BAR2 has been selected. function [0:0] selected2; input [31:0] addr; if ((config_mem[6] & BAR2_MASK) == 0) selected2 = 0; else if ((addr & BAR2_MASK) == (config_mem[6] & BAR2_MASK)) selected2 = 1; else selected2 = 0; endfunction // handle for accessing the memory file. integer memory_fd; // Address that is collected from the address phase // of a PCI transaction. reg [31:0] address; reg [31:0] write_mask; reg [31:0] write_val; reg pending_flag = 0; // These parameters control the memory device' tendency to issue a // retry on a transaction. reg [31:0] retry_rate = 0; reg [31:0] discon_rate = 0; // Handle a PCI reset by resetting drivers and config space. task do_reset; begin pending_flag = 0; AD_reg = 0; AD_en = 0; C_BE_en = 0; FRAME_en = 0; IRDY_en = 0; TRDY_en = 0; STOP_en = 0; DEVSEL_en = 0; PAR_en = 0; config_mem[ 0] = DEVICE_ID; config_mem[ 1] = 32'h00000000; config_mem[ 2] = 32'h05000000; /* RAM memory */ config_mem[ 3] = 32'h00000000; config_mem[ 4] = 32'h00000000 | BAR0_FLAGS; config_mem[ 5] = 32'h00000000 | BAR1_FLAGS; config_mem[ 6] = 32'h00000000 | BAR2_FLAGS; config_mem[ 7] = 32'h00000000; config_mem[ 8] = 32'h00000000; config_mem[ 9] = 32'h00000000; config_mem[10] = 32'h00000000; config_mem[11] = 32'h00000000; config_mem[12] = 32'h00000000; config_mem[13] = 32'h00000000; config_mem[14] = 32'h00000000; config_mem[15] = 32'h00000000; end endtask // do_reset task do_configuration_read; begin AD_reg = config_mem[(AD&32'hff) >> 2]; TRDY_reg <= 0; DEVSEL_reg <= 0; @(posedge CLK) ; AD_en <= 1; TRDY_en <= 1; DEVSEL_en <= 1; @(posedge CLK) ; // Parity bit is delayed one clock. PAR_en <= 1; PAR_reg <= ^{AD_reg, C_BE}; while (IRDY == 1) @(posedge CLK) ; TRDY_reg <= 1; TRDY_en <= 0; DEVSEL_reg <= 1; DEVSEL_en <= 0; @(posedge CLK) ; AD_en <= 0; PAR_en <= 0; end endtask // do_configuration_read task do_configuration_write; begin // Save the address from the address phase. address = AD; TRDY_reg <= 0; DEVSEL_reg <= 0; // Even fast timing response requires that we wait. @(posedge CLK) ; // Activate TRDY and DEVSEL drivers, and start // waiting for the IRDY. TRDY_en <= 1; DEVSEL_en <= 1; while (IRDY == 1) @(posedge CLK) ; case (address[7:2]) 6'b000000: /* device ID is read-only */; 6'b000010: /* class code is read-only */; // Mask BAR0/1, and leave the other BARs zero. 6'b000100: config_mem[6'b000100] = AD & BAR0_MASK | BAR0_FLAGS; 6'b000101: config_mem[6'b000101] = AD & BAR1_MASK | BAR1_FLAGS; 6'b000110: config_mem[6'b000110] = AD & BAR2_MASK | BAR2_FLAGS; 6'b000111: /* BAR3 not implemented. */ ; 6'b001000: /* BAR4 not implemented. */ ; 6'b001001: /* BAR5 not implemented. */ ; 6'b001010: /* CIS not implemented. */ ; 6'b001011: /* subsystem id read-only */; 6'b001101: /* reserved */ ; 6'b001110: /* reserved */ ; default: config_mem[address[7:2]] = AD; endcase // case(addr) TRDY_reg <= 1; TRDY_en <= 0; STOP_reg <= 1; STOP_en <= 0; DEVSEL_reg <= 1; DEVSEL_en <= 0; end endtask // do_configuration_write integer retry_tmp; reg [31:0] read_tmp; reg parity_bit; task do_memory_read; input bar_flag; begin address = AD & ~BAR0_MASK; // Fast timing, respond immediately DEVSEL_reg <= 0; DEVSEL_en <= 1; @(posedge CLK) ; // advance to turnaround cycle retry_tmp = $random % retry_rate; if ((retry_rate > 0) && ((retry_tmp) == 0)) begin TRDY_reg <= 1; STOP_reg <= 0; TRDY_en <= 1; STOP_en <= 1; @(posedge CLK) ; // Drive STOP# for at least one CLK while (FRAME == 0) @(posedge CLK) ; // Wait for the master to give up. // Clean up the drivers. TRDY_en <= 0; STOP_en <= 0; DEVSEL_en <= 0; disable do_memory_read; end TRDY_reg <= 0; TRDY_en <= 1; STOP_reg <= 1; STOP_en <= 1; AD_en <= 1; // Read the addressed value, read_tmp = $pci_memory_peek(memory_fd, address, bar_flag); AD_reg <= read_tmp; parity_bit <= ^{read_tmp, C_BE}; // and go to the next address. address <= address + 4; // Keep reading so long as the master does not // stop the transaction. The TRDY_reg is there to // catch the case that the frame goes away right // away. (The TRDY_reg <= 0 above doesn't happen // until the @posedge inside this loop.) The ~STOP // prevents the TRDY_reg from a disconnect hanging // this loop.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -