Advanced Topic

Here, we will discuss how data is transferred between the software and the FPGA hardware in Rapid HDL.

Each FPGA will require different communication methods. This method is specific to the PicoComputing E-12 FPGA, which plugs into the PCMCIA slot of a laptop.

It should be possible to use Rapid HDL with any FPGA by using reverse engineering and adherence to the same class interfaces shown here.

Pico E-12 FPGA Starting Point

First, consider the firmware that comes with the Pico E-12, before Rapid HDL is involved.
  • On the FPGA side, there is host firmware hardware that cannot be modified.
  • On the FGPA side, there is a sample Verilog project that can be used to synthesize a bit file to the FPGA.
  • On the host PC side, there are proprietary drivers that cannot be modified.
  • On the host PC side, there is a sample c++ program that can be used to communicate with the FPGA.

From the Host PC Side

Our objective is to create a simple interface to the Pico Card.

The PicoInvoke project is used to generate PicoInvoke.dll. PicoInvoke.dll wraps up c++ library available from Pico for communicating with the FPGA. This DLL exposes its public functions using the _declspec( dllexport ) specification. These public functions may then be called from .Net using PInvoke.

The c++ code is shown below.

PicoInvoke.cpp

// PicoInvoke.cpp : Defines the entry point for the DLL application.
//

//stdafx stuff
#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include "pico_errors.h"
#include "gstring.h"
#include "PicoXface.h"

void Message(char * message);

_declspec( dllexport ) BOOL CreatePicoXface(LPCTSTR program_name);
_declspec( dllexport ) UINT GetLastErrorMessage(LPTSTR error_message_out);
_declspec( dllexport ) UINT GetLastErrorCode();
_declspec( dllexport ) BOOL GetCurrentImage(LPTSTR image_name_out);
_declspec( dllexport ) BOOL Reboot(LPCSTR image_name, BOOL run_linked);
_declspec( dllexport ) UINT ReadDevice(UINT address);
_declspec( dllexport ) void WriteDevice(UINT address, UINT data);
_declspec( dllexport ) void WriteDeviceArray(UINT address, USHORT * data, UINT length);
_declspec( dllexport ) void ReadDeviceArray(UINT address, USHORT * data, UINT length);


static BOOL is_pico_connected = false;
static CHAR last_error_message[1024] = "";
static UINT last_error_code = 0;
static PicoXface    *picoXfaceP = NULL;
static LPCTSTR g_program_name = "";

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		picoXfaceP = NULL;
		break;
	}
    return TRUE;
}

void Message(char * message)
{
	MessageBox(NULL,message,"PicoInvoke.DLL",MB_OK);
}

BOOL CreatePicoXfaceDLL(LPCTSTR program_name)
{
	cGString cs;
	int erC;
	const char * erP="";

	char erBuf[100],      //for output error message from CreatePicoXface()  
    *erParamP,        //for error messages  
    *paramsP=NULL;    //parameters to CreatePicoXface  
 
		//cGString      cs;  
 

	g_program_name = program_name;

     //ENTRY PicoXface *CreatePicoXface(const char *createParamsP, char *erMsgBufP, int erMsgSize, int *errP, uint32_t options)

	if( (picoXfaceP = CreatePicoXface(program_name,erBuf,sizeof(erBuf),&erC)) == NULL)
	{
		//if (erC != 7214)
		erP = (const char *)erBuf;
		wsprintf(last_error_message,"Pico Connection Error: [%s] {%s}", (const CHAR *)erBuf, (const CHAR *)PicoErrors_Interpret(erC));
		last_error_code = erC;
		is_pico_connected = FALSE;
		//Message((char *)last_error_message);
		return FALSE;		
	}
	is_pico_connected = TRUE;
	return TRUE;
}

BOOL CreatePicoXface(LPCTSTR program_name)
{
	return CreatePicoXfaceDLL(program_name);
}

UINT GetLastErrorMessage(LPTSTR error_message_out)
{
	wsprintf(error_message_out,"%s",last_error_message);
	return last_error_code;
}

UINT GetLastErrorCode()
{
	return last_error_code;
}

BOOL GetCurrentImage(LPTSTR image_name_out)
{
	int errP = 0;
	if (!is_pico_connected || picoXfaceP == NULL)
	{
		if((BOOL)CreatePicoXfaceDLL((LPCTSTR)g_program_name)!=TRUE) return FALSE;
	}

	cGString cgsImg = picoXfaceP->GetCurrentImage(&errP);
	wsprintf(image_name_out,"%s",cgsImg.c_str());

	last_error_code = errP;
	if (last_error_code != 16384 && last_error_code != 2867200 && last_error_code != 0)
	{
		cGString cgsError = PicoErrors_Interpret(errP);
		wsprintf(last_error_message,"%s",cgsError.c_str());
		return FALSE;
	}

	return TRUE;
}

BOOL Reboot(LPCSTR image_name, BOOL run_linked)
{
	int err;
	if (!is_pico_connected || picoXfaceP == NULL)
	{
		if(CreatePicoXfaceDLL(g_program_name)!=TRUE) return FALSE;
	}

	err = picoXfaceP->Reboot(image_name,run_linked);

	last_error_code = err;
	if (last_error_code != 0)
	{
		cGString cgsError = PicoErrors_Interpret(last_error_code);
		wsprintf(last_error_message,"%s",cgsError.c_str());
		return FALSE;
	}
	return TRUE;
}

UINT ReadDevice(UINT address)
{
	int err;
	char test[10];
	UINT data;

	if (!is_pico_connected || picoXfaceP == NULL)
	{
		if(!CreatePicoXfaceDLL(g_program_name)) return 0;
	}

	err = picoXfaceP->ReadDevice(address,&data,4);
	
	last_error_code = err;
	if (last_error_code < 0)
	{
		cGString cgsError = PicoErrors_Interpret(last_error_code);
		wsprintf(last_error_message,"%s",cgsError.c_str());
		return 0;
	}
	return data;
}

void WriteDevice(UINT address, UINT data)
{
	int err;
	char test[10];

	if (!is_pico_connected || picoXfaceP == NULL)
	{
		if(!CreatePicoXfaceDLL(g_program_name)) return;
	}

	err = picoXfaceP->WriteDevice(address,&data,4);
	
	last_error_code = err;
	if (last_error_code < 0)
	{
		cGString cgsError = PicoErrors_Interpret(last_error_code);
		wsprintf(last_error_message,"%s",cgsError.c_str());
		return;
	}
	return;
}

void WriteDeviceArray(UINT address, USHORT * data, UINT length)
{	
	int err;

	if (!is_pico_connected || picoXfaceP == NULL)
	{
		if(!CreatePicoXfaceDLL(g_program_name)) return;
	}

	err = picoXfaceP->WriteDevice(address,data,length*2);
	
	last_error_code = err;
	if (last_error_code < 0)
	{
		cGString cgsError = PicoErrors_Interpret(last_error_code);
		wsprintf(last_error_message,"%s",cgsError.c_str());
		Message(last_error_message);
		return;
	}
	return;
}

void ReadDeviceArray(UINT address, USHORT * data, UINT length)
{	
	int err;
	USHORT data_short;

    if (!is_pico_connected || picoXfaceP == NULL)
	{
		if(!CreatePicoXfaceDLL(g_program_name)) return;
	}

    //for (int idx = 0; idx < length; idx++)
	//{
		//Message("Hello");
		err = picoXfaceP->ReadDevice(address,data,length*2);
		last_error_code = err;
		if (last_error_code < 0)
		{
			cGString cgsError = PicoErrors_Interpret(last_error_code);
			wsprintf(last_error_message,"%s",cgsError.c_str());
			Message(last_error_message);
			return;
		}
		//data[idx] = data_short;
	//}	
}

The .Net c# code that pinvokes PicoInvoke is located in Rapid HDL's PicoInvoke.cs class.

PicoInvoke.cs

using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Text;

namespace PicoInterface
{
    /// <summary>
    /// Summary description for PicoInvoke.
    /// </summary>
    sealed class PicoInvoke
    {
        [DllImport("PicoInvoke.dll", EntryPoint = "#1")]
        public static extern Boolean CreatePicoXface(String program_name);

        [DllImport("PicoInvoke.dll", EntryPoint = "#2")]
        public static extern Boolean GetCurrentImage(StringBuilder image_name_out);

        [DllImport("PicoInvoke.dll", EntryPoint = "#3")]
        public static extern Int32 GetLastErrorCode();
        
        [DllImport("PicoInvoke.dll", EntryPoint = "#4")]
        public static extern Int32 GetLastErrorMessage(StringBuilder program_name);

        [DllImport("PicoInvoke.dll", EntryPoint = "#5")]
        public static extern Int32 ReadDevice(Int32 address);

        [DllImport("PicoInvoke.dll", EntryPoint = "#6")]
        public static extern Int32 ReadDeviceArray(Int32 address, ushort[] data, Int32 length);

        [DllImport("PicoInvoke.dll", EntryPoint = "#7")]
        public static extern Boolean Reboot(string image_name, Boolean run_linked);


        [DllImport("PicoInvoke.dll", EntryPoint = "#8")]
        public static extern Int32 WriteDevice(Int32 address, Int32 data);

        [DllImport("PicoInvoke.dll", EntryPoint = "#9")]
        public static extern Int32 WriteDeviceArray(Int32 address, ushort[] data, Int32 length);


        public static string GetLastError()
        {
            StringBuilder oErrorMessage = new StringBuilder(1024, 1024);
            int iLastError = GetLastErrorMessage(oErrorMessage);
            if (oErrorMessage.ToString().Substring(oErrorMessage.ToString().Length - 13, 13) != "Unknown error")
            {
                return "Error Code: " + iLastError.ToString() + "\n" + oErrorMessage.ToString();
            }
            else
                return "Unknown Error";
        }
    }
}

PicoInvoke.cs is merely a class that wraps up PInvoke functions to call PicoInvoke.dll. Perhaps it is easiest to see what data transfer options are available by examining the functions here.
  • CreatePicoXFace( BitFileName) - Connect to the FPGA and run the specified bitfile
  • GetCurrentImage() - Find out which bitfile is currently loaded in the FPGA
  • GetLastErrorCode() - Report the last driver error code
  • GetLLastErrorMessage() - Report the last error message as a string
  • ReadDevice( Address) - Read a 16 bit value from a device address having 16 bits
  • ReadDeviceArray( Address, Destination Buffer, Length) - Fill a 16 bit arrays starting from a 16 bit device address
  • Reboot( BitFileName ) - Restart the FPGA and run the specified bitfile
  • WriteDevice( Address) - Write a 16 bit value to a 16 bit device address
  • WriteDeviceArray( Address , Source Buffer, Length) - Write a 16 bit array to device starting at 16 bit device address


PicoInvoke.cs handles generic communication with the FPGA. If you can create something similar for your FPGA, then you can plug it directly into Rapid HDL.

Important Point

The communication model used by RapidHDL reads/writes 16 bit values to/from device addresses on the FPGA. On the FPGA side, Rapid HDL hardware hooks into the address and data bus to co-ordinate communication.

PicoIO class

The class defined in PicoInvoke.cs is called exclusively by the PicoIO class. The PicoIO class makes assumptions about the way that Rapid HDL has tapped into the FPGA device's address and data bus.

The code for PicoIO.cs is shown below. Device Address base values are defined near the top of the file. These addresses are important because they match the addresses where Rapid HDL is tapped into the FPGA device bus. The Rapid HDL hardware controls things like starting and stopping the clock or applying a reset signal. The commands to execute these control operations are passed over the device address and data buses.

PicoIO.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace PicoInterface
{
    public class PicoIO
    {
        public const int BASE_ADDR = 184549376;
        public const int IO_ADDR = 184614912;
        public const int IO_STATUS_REG_ADDR = BASE_ADDR + 6;

        public static string Attach(string sImageName)
        {
            string sResult = "";

            StringBuilder buffer = new StringBuilder(1024, 1024);
            if (!PicoInvoke.CreatePicoXface("c:\\pico\\bin\\"))
            {
                return PicoInvoke.GetLastError();
            }

            PicoInvoke.GetCurrentImage(buffer);
            if (buffer.ToString().Length == 0)
                return "Error reading getting pico image file name \n" + PicoInvoke.GetLastError();
            sResult += buffer.ToString() + "\n";

            PicoInvoke.Reboot(sImageName, true);

            if (!PicoInvoke.GetCurrentImage(buffer))
            if (buffer.ToString().Length == 0)
                return "Error reading getting pico image file name \n" + PicoInvoke.GetLastError();
            
            sResult += buffer.ToString() + "\n";
            return sResult;
        }

        public static void Reset()
        {
            PicoInvoke.WriteDevice(BASE_ADDR, 3);
        }

        public static void StopClock()
        {
            PicoInvoke.WriteDevice(BASE_ADDR, 0);
        }

        public static void StartClock()
        {
            //string cyclesBinary = RapidHDL.Conversion.IntToBinaryString(16383, 14) + "01";
            string cyclesBinary = RapidHDL.Conversion.IntToBinaryString(1024, 14) + "01";
            PicoInvoke.WriteDevice(BASE_ADDR, RapidHDL.Conversion.StringBinaryToInt(cyclesBinary));
        }

        public static void StartClock(int cycles)
        {
            int loops = 1;

            if (cycles > 16000)
            {
                loops = cycles / 16000;
                cycles = 1600;
            }

            double secs = cycles / 25.0 / 1000000.0;
            int delay = (int)(secs * 1000);
            delay += 1;

            //JNA modify here if base clock speed changes!
            string cyclesBinary = RapidHDL.Conversion.IntToBinaryString(cycles * 2, 14) + "01";

            for (int loopIdx = 0; loopIdx < loops; loopIdx++)
            {
                PicoInvoke.WriteDevice(BASE_ADDR, RapidHDL.Conversion.StringBinaryToInt(cyclesBinary));
                //System.Threading.Thread.Sleep(delay);
            }
        }

        public static void WriteData(int value)
        {
            string valueBinary = RapidHDL.Conversion.IntToBinaryString(value, 13) + "100";
            PicoInvoke.WriteDevice(BASE_ADDR, RapidHDL.Conversion.StringBinaryToInt(valueBinary));
        }

        public static void StoreAddress(int addr)
        {
            // this loads the address to be used by WriteData
            string valueBinary = RapidHDL.Conversion.IntToBinaryString(addr, 13) + "000";
            PicoInvoke.WriteDevice(BASE_ADDR, RapidHDL.Conversion.StringBinaryToInt(valueBinary));
        }


        public static int Read(int addr)
        {
            string addrBinary = RapidHDL.Conversion.IntToBinaryString(addr, 14) + "10";
            PicoInvoke.WriteDevice(BASE_ADDR, RapidHDL.Conversion.StringBinaryToInt(addrBinary));
            return PicoInvoke.ReadDevice(BASE_ADDR);
        }

        public static int ReadIOStatusReg()
        {
            return PicoInvoke.ReadDevice(IO_STATUS_REG_ADDR);            
        }

        public static void WriteArray(ushort[] data, int length)
        {
            PicoInvoke.WriteDeviceArray(IO_ADDR, data, length);
        }

        public static void ReadArray(ushort[] data, int length)
        {
            PicoInvoke.ReadDeviceArray(IO_ADDR, data, length);            
        }



        /*public static void DisplayAddr(int addr)
        {
            int data = Read(addr);
            string binary = RapidHDL.Conversion.IntToBinaryString(data, 16);
            Console.WriteLine(addr.ToString() + ": " + binary + " {" + data.ToString() + "}");
        }*/
    }
}

  • Base Address Data Encoding

        public const int BASE_ADDR = 184549376;

The last three bits of the data written to the base address of the device are control codes. All control of the Rapid Hardware is effectuated by writing to the base address.
  • 00 - Stop Clock
  • 01 - Start Clock
    • Bits 15-3 bits specify the number of clock cycles
    • Writing 1024 will cause the clock to run indefinitely
  • 10 - Read Signal View
    • Bits 15-2 specify the address to read
  • 11 - Reset
  • 000 - Store Address
    • Bits 15-3 specify the address to be written
  • 100 - Write Data
    • Bits 15-3 specify the data to be written

Data read and written through the device port at the base address will be limited to SignalViews and SignalSources. SignalViews are only valid when the clock is stopped, so speed of this port is not critical. Note that the address width for signal views and signal sources is limited to 13 bits, limiting the total number of signal view and signal source addresses that can be used in a single Hardware design to about 8000. Also, notice that the data written to the device is reduced by 3 bits to 13 bits. However, there is not a 13 bit limitation on the maximum width of a signal view. If the signal view requires more than 13 bits, then it will automatically be assigned more than one address.
  • Arrays of Data

        public const int IO_ADDR = 184614912;
        public const int IO_STATUS_REG_ADDR = BASE_ADDR + 6;

Writing and reading arrays of data is faster than writing to the Base Address and allows larger chunks of data to be passed to the FPGA.

Blocks of 6 data words are read or written to the IO_ADDR device address. This data is 16 bits wide. In the Rapid HDL firmware, the data is posted to a FIFO buffer, or read from a FIFO buffer. The FIFO buffers provide higher speed communication to the FPGA. The FIFO buffers are available even when the clock is running, so the executing hardware is not interrupted by IO with the software.

The IOSTATUSREG_ADDR is the device location for reading the state of the FPGA's FIFO buffers.

Other Rapid Hardware classes related to FPGA communication

The following classes are worth examining to understand the communication that takes place between the FPGA and the Software:
  • FPGAInterface.cs - a base class
  • PicoInterface.cs - inherits FPGAInterface.cs
  • SignalView.cs
  • SignalSource.cs
  • IOInterface.cs - reads and writes packets of data in array format to and from the FPGA FIFO's
  • IO Namespace - FIFO packet formats and packet processing

The FPGA Side

MemoryPeripherals.v

The primary hook into the Pico firmware is located in MemoryPeripherals.v.

Signals for Rapid HDL.

//--------Rapid HDL wires ----//
wire [12:0] test_bus_read;
wire [12:0] test_bus_write;
wire [15:0] rhdl_dout; // the output data bus for RHDL stuff


//---------Rapid HDL wires SDRAM ------------//
`ifdef ENABLE_RAM_PADS
	wire SDRAMClock;
	wire ram_oe;
	wire [23:0] SYS_ADDR;
	wire [31:0] SYS_DATA_O;
	wire [31:0] SYS_DATA_I;
	wire SYS_WRITE;
	wire SYS_START;
	wire SYS_BUSY;
`endif

In this section of Verilog code, the rhdl_perhipheral module is connected to the device ports of the FPGA.

//---------Extended Peripheral Bus------------//
rhdl_pico_peripheral rhdl_peripheral (    
	.CLK_I(CFClock), 
   .CLOCK1(CLOCK2),	
	.READ_I(MemRead),     
	.WRITE_I(MemWrite),     
	.ADDR_I(MemAddress),     
	.DATA_I(DataIn),     
	.DATA_O(rhdl_dout),  
	.RESET_O(rhdl_reset),
	.TEST_BUS_READ_I(test_bus_read), 
	.TEST_BUS_WRITE_O(test_bus_write) 
`ifdef ENABLE_RAM_PADS, 
	.RAM_CLK_I(SDRAMClock),
	.SYS_BUSY_I(SYS_BUSY), 
	.SYS_DATA_OUT_I(SYS_DATA_O), 
	.SYS_DATA_IN_O(SYS_DATA_I), 
	.SYS_ADDR_O(SYS_ADDR),	
	.SYS_WRITE_O(SYS_WRITE), 
	.SYS_START_O(SYS_START)
`endif

Aditionally, the firmware was modified to wire the SDRAM ports into the rhdl_peripheral.

//------- Rapid HDL SDRAM Controller --------------//
//`ifdef ENABLE_RAM_PADS  
   
	//100 MHz Clock Generator
	SDRAMClockGen SDRAMClockGen(.CLOCKIN(CLOCK1), .ClockOut(SDRAMClock));
	
	mobile_sdram_ctl mobile_sdram_ctl (
	    .SYS_RESET_I(rhdl_reset), 
	    .SYS_DATA_I(SYS_DATA_I), 
	    .SYS_DATA_O(SYS_DATA_O), 
	    .SYS_ADDR_I(SYS_ADDR), 
	    .SYS_WRITE_I(SYS_WRITE), 
	    .SYS_START_I(SYS_START), 
	    .SYS_BUSY_O(SYS_BUSY), 
	    .SDR_CLK_I(SDRAMClock), 
	    .SDR_CKE_O(RamClkE), 
	    .SDR_ADDR_O(RamAddr), 
	    .SDR_BA_O(RamBA), 
	    .SDR_CS_O(RamCS), 
	    .SDR_RAS_O(RamRAS), 
	    .SDR_CAS_O(RamCAS), 
	    .SDR_WE_O(RamWE), 
	    .SDR_DQM_O(RamDM), 
	    .SDR_DQ_O(RamDOut), 
	    .SDR_DQ_I(RamDIn), 
	    .SDR_OE_O(ram_oe),
	    .TEST_BUS_WRITE_I(test_bus_write),
	    .TEST_BUS_READ_O(test_bus_read)
	    );

	    assign RamClk = SDRAMClock;
	    assign RamDT = {ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe,ram_oe};
//`endif

Here, the Rapid HDL data bus is wired to the output of the device bus.

//---------------DataOut Bus------------------//
//All of the DataOut signals for the peripheral modules are combined here.

assign DataOut[15:0] =                   (FlashDataOut[15:0]          |
   `ifndef DISABLE_TUPLE                  TupleDecoderDataOut[15:0]   | `endif
   `ifdef ENABLE_CARD_INFO                CardInfoDataOut[15:0]       | `endif
   `ifdef ENABLE_GPIO                     GPIODataOut[15:0]           | `endif       
   `ifdef ENABLE_SYSGEN                   SysGenDataOut[15:0]         | `endif
   `ifdef ENABLE_MDIO                     MDIODataOut[15:0]           | `endif
   `ifdef ENABLE_SIMULATED_BUS_MASTERING  BMtestDataOut[15:0]         | `endif 
   `ifdef ENABLE_USER_PICOBUS	          UserModulePicoDataOut[15:0] | `endif 
													   rhdl_dout                    |
                                          CPLDDataOut[15:0]);

rhdlpicoperipheral.v

The rhdlpicoperipheral file is permanently hooked into the Pico firmware. It combines 4 major components.
  • rhdlpicoctl.v
    • Permanent controller of Rapid Hardware
    • Handles things like the clock
  • rhdlpicoi.v
    • Permanent controller of Data FIFO
  • rhdl_ExternalInterfaceComponent
    • This component is generated new each time Rapid HDL generates the hardware.
    • Handles connections to signal views and signal sources
  • rhdlpicosdram_io.v
    • Permanent connection to SDRAM controller

module rhdl_pico_peripheral(CLK_I, CLOCK1, READ_I, WRITE_I, ADDR_I, DATA_I, DATA_O, RESET_O, TEST_BUS_WRITE_O, TEST_BUS_READ_I
	`ifdef ENABLE_RAM_PADS, RAM_CLK_I, SYS_BUSY_I, SYS_DATA_OUT_I, SYS_DATA_IN_O, SYS_ADDR_O, SYS_WRITE_O, SYS_START_O `endif // ENABLE_RAM_PADS 
);
    input CLK_I;
	 input CLOCK1;
    input READ_I;
    input WRITE_I;
    input [31:0] ADDR_I;
    input [15:0] DATA_I;
    output [15:0] DATA_O;
    output RESET_O;
    output [12:0] TEST_BUS_WRITE_O;
    input [12:0] TEST_BUS_READ_I;    
	 
	 wire [12:0] test_bus;
    
    `ifdef ENABLE_RAM_PADS
		input RAM_CLK_I;
    	input SYS_BUSY_I;
    	input [31:0] SYS_DATA_OUT_I;
    	output [31:0] SYS_DATA_IN_O;
    	output [31:0] SYS_ADDR_O;
    	output SYS_WRITE_O;
    	output SYS_START_O;
    `endif

    
    
    wire main_clk_en;
    wire main_clk;
    wire main_reset;
    wire main_write;
    
    wire [15:0] data_ctl_o;
    wire [15:0] data_io_o;	 
    
    wire [15:0] read_data;
    wire [12:0] write_data;
    wire [12:0] write_addr;
    wire [13:0] read_addr;
	 	
	 wire [15:0] fifo_to_bus;
	 wire [17:0] fifo_from_bus;
	 wire fifo_write;
	 wire fifo_read;
	 wire [1:0] fifo_status;

	wire ram_write;
	wire ram_read;
	wire [63:0] ram_data_in;
	wire [41:0] ram_data_out;
	wire [1:0] ram_status;

    //BUFGCE ClockOutBuf(.I(RAM_CLK_I), .CE(main_clk_en), .O(main_clk)); 
	 BUFGCE ClockOutBuf(.I(CLOCK1), .CE(main_clk_en), .O(main_clk)); 
        
assign RESET_O = main_reset;
        
rhdl_pico_ctl rhdl_pico_ctlr (
    .CLK_I(CLK_I), 
    .READ_I(READ_I), 
    .WRITE_I(WRITE_I), 
    .ADDR_I(ADDR_I), 
    .DATA_I(DATA_I), 
    .MUX_DATA_I(read_data), 
    .CLK_EN_O(main_clk_en), 
    .RESET_O(main_reset), 
    .START_O(main_start),
    .WRITE_O(main_write), 
    .DATA_O(data_ctl_o), 
    .WRITE_ADDR_O(write_addr),
    .READ_ADDR_O(read_addr), 
    .WRITE_DATA_O(write_data)
    );

rhdl_pico_io rhdl_io (
    .BUS_CLK_I(CLK_I), 
    .RHDL_CLK_I(main_clk), 
    .RESET_I(main_reset), 
    .READ_I(READ_I), 
    .WRITE_I(WRITE_I), 
    .ADDR_I(ADDR_I), 
    .DATA_I(DATA_I), 
    .WRITE_FIFO_I(fifo_write), 
    .READ_FIFO_I(fifo_read), 
    .FIFO_DATA_I(fifo_to_bus), 
    .DATA_O(data_io_o), 
    .FIFO_DATA_O(fifo_from_bus),
	 .FIFO_STATUS_O(fifo_status)
	 //.TEST_BUS_O(test_bus)
    );

	     
rhdl_ExternalInterfaceComponent rhdl_interface (
	.MAIN_READ_ADDR_I(read_addr),
    .MAIN_CLK_I(main_clk), 
    .MAIN_RESET_I(main_reset), 
    .MAIN_WRITE_ADDR_I(write_addr), 
    .MAIN_WRITE_I(main_write), 
    .MAIN_START_I(main_start), 
    .MAIN_DATA_I(write_data), 
    .MAIN_DATA_O(read_data),
	 .FIFO_O(fifo_to_bus),
	 .FIFO_I(fifo_from_bus),
	 .FIFO_WRITE_O(fifo_write),
	 .FIFO_READ_O(fifo_read),
	 .FIFO_STATUS_I(fifo_status),
	 .RAM_O(ram_data_in),
	 .RAM_I(ram_data_out),
	 .RAM_WRITE_O(ram_write),
	 .RAM_READ_O(ram_read),
	 .RAM_STATUS_I(ram_status),
    .TEST_BUS_WRITE_O(TEST_BUS_WRITE_O),
    .TEST_BUS_READ_I(test_bus)
);
  //`ifdef ENABLE_RAM_PADS									    

rhdl_pico_sdram_io rhdl_sdram_io (
    .MEM_CLK_I(RAM_CLK_I), 
    .RHDL_CLK_I(main_clk), 
    .RESET_I(RESET_I), 
    .WRITE_FIFO_I(ram_write), 
    .READ_FIFO_I(ram_read), 
    .FIFO_DATA_I(ram_data_in), 
    .FIFO_DATA_O(ram_data_out), 
    .FIFO_STATUS_O(ram_status),     
	 .TEST_BUS_O(test_bus), 
    .SYS_BUSY_I(SYS_BUSY_I), 
    .SYS_DATA_I(SYS_DATA_OUT_I), 
    .SYS_DATA_O(SYS_DATA_IN_O), 
    .SYS_ADDR_O(SYS_ADDR_O), 
    .SYS_WRITE_O(SYS_WRITE_O), 
    .SYS_START_O(SYS_START_O)
    );
//`endif

   
    /*
io_test io_test (
    .CLK_I(CLK_I), 
    .READ_I(READ_I), 
    .WRITE_I(WRITE_I), 
    .ADDR_I(ADDR_I), 
    .DATA_I(DATA_I), 
    .DATA_O(DATA_O)
    );
*/

	assign DATA_O = data_io_o | data_ctl_o;    
endmodule

rhdlpicoctl.v

This Verilog module handles requests written to the BASE_ADDRESS, including the Clock generation, Reset generation, and decoding the connection Signal Views and Signal Encoding.

module rhdl_pico_ctl(CLK_I, READ_I, WRITE_I, ADDR_I, DATA_I, MUX_DATA_I, CLK_EN_O, RESET_O, START_O, WRITE_O, DATA_O, WRITE_ADDR_O, READ_ADDR_O, WRITE_DATA_O);
    input CLK_I;
    input READ_I;
    input WRITE_I;
    input [31:0] ADDR_I;
    input [15:0] DATA_I;
    input [15:0] MUX_DATA_I;
    
    output CLK_EN_O;
    output RESET_O;
    output START_O;
    output WRITE_O;
    output [15:0] DATA_O;
    output [12:0] WRITE_ADDR_O;
    output [13:0] READ_ADDR_O;
    output [12:0] WRITE_DATA_O;

	reg CLK_EN_O;
	reg RESET_O;
	reg START_O;
	//reg WRITE_O;

	reg [12:0] WRITE_ADDR_O;
	
	`define IO_BASE_ADDR 32'h0B000000


reg [13:0] clk_count;
reg [15:0] data_reg_o;


wire select;
wire clk_stopped;
reg clk_no_stop;

assign select = (`IO_BASE_ADDR == ADDR_I);
assign clk_stopped = (clk_count == 14'b0);

always @(posedge CLK_I)
begin
	if (WRITE_I && select)
	begin
		CLK_EN_O <= 0;
		case (DATA_I[1:0])
		3:    //reset
		begin
			RESET_O <= 1;
			START_O <= 0;
			clk_count <= 8; //4; //8 JNA
			data_reg_o <= 16'h3;
			WRITE_ADDR_O <= WRITE_ADDR_O;
			clk_no_stop <= 0;
		end

		2: 	// read
		begin
			RESET_O <= RESET_O;
			START_O <= START_O;
			clk_count <= 0;
			data_reg_o <= MUX_DATA_I;
			WRITE_ADDR_O <= WRITE_ADDR_O;
			clk_no_stop <= 0;
		end

		1:		// start clock
		begin
			RESET_O <= 0;
			START_O <= 1;
			//clk_count <= DATA_I[15:2];
			clk_count[13:1] <= DATA_I[14:2];
			clk_count[0] <= 1'b0;
			data_reg_o <= 16'h1;
			WRITE_ADDR_O <= WRITE_ADDR_O;
			if (DATA_I[15:2] == 1024)
			begin
				clk_no_stop <= 1;
			end
			else
			begin
				clk_no_stop <= 0;
			end
		end
		
		0:   // write
		begin
			RESET_O <= RESET_O;
			START_O <= START_O;
			clk_count <= 0;
			clk_no_stop <= 0;
			if (DATA_I[2] == 0)
			begin
				data_reg_o <= 16'h4;
				WRITE_ADDR_O <= DATA_I[15:3];
			end
			else
			begin
				data_reg_o <= 16'h5;
				WRITE_ADDR_O <= WRITE_ADDR_O;
			end						
		end
		endcase
	end
	else
	begin
		clk_no_stop <= clk_no_stop;
		WRITE_ADDR_O <= WRITE_ADDR_O;
		data_reg_o <= data_reg_o;
		if (!clk_stopped)
		begin
			CLK_EN_O <= 1;
			if (clk_no_stop)
				clk_count <= 1;
			else
				clk_count <= clk_count - 1;
				
			START_O <= 0;
			RESET_O <= RESET_O;
			clk_no_stop <= clk_no_stop;
		end
		else
		begin
			CLK_EN_O <= 0;
			clk_count <= clk_count;
			clk_no_stop <= 0;
			START_O <= 0;
			RESET_O <= 0;
		end
	end
end


assign DATA_O = (READ_I && select) ? data_reg_o : 16'h0;
assign READ_ADDR_O = DATA_I[15:2];

assign WRITE_O = WRITE_I && select && (DATA_I[2:0] == 3'b100);
assign WRITE_DATA_O = DATA_I[15:3];

endmodule

rhdlpicoio

The rhdlpicoio.v module connects the fifo buffers to the IO_ADDR of the device bus.

`timescale 1ns / 1ps
module rhdl_pico_io(
	 input BUS_CLK_I,
    input RHDL_CLK_I,
    input RESET_I,
    input READ_I,
    input WRITE_I,
    input [31:0] ADDR_I,
    input [15:0] DATA_I,

	 input WRITE_FIFO_I,
	 input READ_FIFO_I,
	 input [15:0] FIFO_DATA_I,
	 
    output [15:0] DATA_O,
	 output [17:0] FIFO_DATA_O,
	 output [1:0] FIFO_STATUS_O,
	 output [12:0] TEST_BUS_O
);


`define IO_ADDR 16'h0B01
`define IO_STATUS_REG_ADDR 32'h0B000006

wire select;
wire select_status_reg;

wire [17:0] data_in_fifo;

wire external_fifo_full;
wire external_fifo_empty;
wire internal_fifo_full;
wire internal_fifo_empty;
wire [15:0] internal_fifo_data;

wire [15:0] io_data;
wire [15:0] status_data;

wire [12:0] test_bus;
wire [12:0] no_test_bus;

reg [1:0] status_reg;

reg [12:0] count;

reg internal_read_select_last;
reg internal_read_inc;

reg external_write_select_last;
reg external_write_inc;

reg [17:0] write_data;

wire [17:0] debug;

assign FIFO_DATA_O = debug;//{2'b11,debug[15:0]};
//6 = 64 ushort length, 16 word length (64 bit)
fifo1 #(18,4) from_external_fifo (
    .rdata(debug), 
    .wfull(external_fifo_full), 
    .rempty(external_fifo_empty), 
    .wdata(write_data), 
    .winc(external_write_inc), 
    .wclk(BUS_CLK_I), 
    .wrst_n(~RESET_I), 
    .rinc(READ_FIFO_I), 
    .rclk(RHDL_CLK_I), 
    .rrst_n(~RESET_I),
	 .TEST_BUS_O(test_bus)
    );

//6 = 128 ushort length, 42 word length (48 bit)
fifo1 #(16,4) from_internal_fifo (
    .rdata(internal_fifo_data), 
    .wfull(internal_fifo_full), 
    .rempty(internal_fifo_empty), 
    .wdata(FIFO_DATA_I), 
    .winc(WRITE_FIFO_I), 
    .wclk(RHDL_CLK_I), 
    .wrst_n(~RESET_I), 
    .rinc(internal_read_inc), 
    .rclk(BUS_CLK_I), 
    .rrst_n(~RESET_I),
	 .TEST_BUS_O(no_test_bus)
  );

//    .rinc(READ_I & select), 

always @(negedge BUS_CLK_I)
begin
	if (RESET_I)
	begin
		count <= 0;
		internal_read_inc <= 0;
		internal_read_select_last <= 0;
		external_write_inc <= 0;
		external_write_select_last <= 0;
	end
	else
	begin
		if (internal_read_select_last)
		begin
			if (~(READ_I && select))
			begin
				internal_read_inc <= 1;
			end
			else
			begin
				internal_read_inc <= 0;
			end
		end
		else
		begin
			internal_read_inc <= 0;
		end
		
		if (!external_write_select_last)
		begin
			if (WRITE_I && select)
			begin
				count <= count + 1;
				external_write_inc <= 1;
			end
			else
			begin
				count <= count;
				external_write_inc <= 0;
			end
		end
		else
		begin
			count <= count;
			external_write_inc <= 0;
		end
		
		internal_read_select_last <= (READ_I && select);
		external_write_select_last <= (WRITE_I && select);
	end
	
   write_data <= {ADDR_I[2:1],DATA_I};
	status_reg <= {external_fifo_full,internal_fifo_empty};
end

/*always @(posedge BUS_CLK_I)
begin
	status_reg <= {1'b1,internal_fifo_empty};
end
*/

assign select = (`IO_ADDR == ADDR_I[31:16]); 
assign select_status_reg = (`IO_STATUS_REG_ADDR == ADDR_I);
assign io_data = (READ_I & select & ~internal_fifo_empty) ? internal_fifo_data : 16'b0;
assign status_data = (READ_I && select_status_reg) ? {1'b1,13'b0,status_reg} : 16'b0;
//
assign DATA_O = io_data | status_data;

assign FIFO_STATUS_O = {internal_fifo_full,external_fifo_empty};

assign TEST_BUS_O = test_bus; //{external_fifo_full,external_fifo_empty,internal_fifo_full,internal_fifo_empty,4'b0110,internal_fifo_data[3:0]};

endmodule

rhdl_ExternalInterfaceComponent

rhdl_ExternalInterfaceComponent.v is generated by Rapid HDL each time the hardware is generated. This file is specific to the hardware being generated. This file is where the data is piped into the Rapid HDL generated hardware.

The rhdl_ExternalInterface.v wires together two main modules...
  • rhdl_ExternalSignalInterface.v - decodes signal sources and signal views
  • rhdl_TopLevelComponent.v - this is the user defined hardware and corresponds to the TopLevel component of the Rapid Hardware

module rhdl_ExternalInterfaceComponent
   (FIFO_I,
   RAM_I,
   RAM_STATUS_I,
   FIFO_STATUS_I,
   MAIN_RESET_I,
   MAIN_READ_ADDR_I,
   MAIN_START_I,
   TEST_BUS_READ_I,
   MAIN_CLK_I,
   MAIN_DATA_I,
   MAIN_WRITE_ADDR_I,
   MAIN_WRITE_I,
   FIFO_O,
   FIFO_READ_O,
   RAM_READ_O,
   RAM_WRITE_O,
   FIFO_WRITE_O,
   MAIN_DATA_O,
   RAM_O,
   TEST_BUS_WRITE_O);

      input [17:0] FIFO_I;
      input [47:0] RAM_I;
      input [3:0] RAM_STATUS_I;
      input [1:0] FIFO_STATUS_I;
      input MAIN_RESET_I;
      input [13:0] MAIN_READ_ADDR_I;
      input MAIN_START_I;
      input [12:0] TEST_BUS_READ_I;
      input MAIN_CLK_I;
      input [12:0] MAIN_DATA_I;
      input [12:0] MAIN_WRITE_ADDR_I;
      input MAIN_WRITE_I;

      output [15:0] FIFO_O;
      output FIFO_READ_O;
      output RAM_READ_O;
      output RAM_WRITE_O;
      output FIFO_WRITE_O;
      output [15:0] MAIN_DATA_O;
      output [63:0] RAM_O;
      output [12:0] TEST_BUS_WRITE_O;

      wire I_ENABLE;
      wire [7:0] io_test_source;
      wire [4:0] TopLevel_counter_COUNT_O;

      wire const_0_v3_out;
      wire const_0_v2_out;
      wire const_0_v5_out;
      wire [15:0] const_0_v4_out;
      wire const_0_v6_out;
      wire [63:0] const_0_v1_out;
      wire [12:0] const_0_v0_out;



//*****  Redundant Wiring  *****

         assign const_0_v3_out = {RAM_WRITE_O};
         assign const_0_v2_out = {RAM_READ_O};
         assign const_0_v5_out = {FIFO_READ_O};
         assign const_0_v4_out = {FIFO_O};
         assign const_0_v6_out = {FIFO_WRITE_O};
         assign const_0_v1_out = {RAM_O};
         assign const_0_v0_out = {TEST_BUS_WRITE_O};

//*****  Subcomponet Declarations  *****



//*****  Skipping   with 0 outputs.  *****


rhdl_Constant const_0_v4 (
      .DATA_O({FIFO_O})
   );

rhdl_Constant2 const_0_v1 (
      .DATA_O({RAM_O})
   );

rhdl_TopLevelComponent TopLevel (
      .main_reset({MAIN_RESET_I}),
      .io_test({io_test_source}),
      .counter_ENABLE_I({I_ENABLE}),
      .main_clock_CLK_I({MAIN_CLK_I}),
      .counter_COUNT_O({TopLevel_counter_COUNT_O})
   );

rhdl_Constant5 const_0_v2 (
      .DATA_O({RAM_READ_O})
   );


//*****  Skipping   with 0 outputs.  *****


rhdl_Constant6 const_0_v6 (
      .DATA_O({FIFO_WRITE_O})
   );

rhdl_Constant7 const_0_v0 (
      .DATA_O({TEST_BUS_WRITE_O})
   );


//*****  Skipping   with 0 outputs.  *****



//*****  Skipping   with 0 outputs.  *****


rhdl_ExternalSignalInterface signal_interface (
      .start_sink({MAIN_START_I}),
      .READ_ADDR_I({MAIN_READ_ADDR_I}),
      .out_mux_DATA_0_I_i({TopLevel_counter_COUNT_O}),
      .RESET_I({MAIN_RESET_I}),
      .CLK_I({MAIN_CLK_I}),
      .WRITE_ADDR_I({MAIN_WRITE_ADDR_I}),
      .DATA_I({MAIN_DATA_I}),
      .WRITE_I({MAIN_WRITE_I}),
      .DATA_O({MAIN_DATA_O}),
      .I_ENABLE_1_o({I_ENABLE}),
      .io_test_source_1_o({io_test_source})
   );


//*****  Skipping   with 0 outputs.  *****


rhdl_Constant10 const_0_v3 (
      .DATA_O({RAM_WRITE_O})
   );

rhdl_Constant11 const_0_v5 (
      .DATA_O({FIFO_READ_O})
   );

// Component Did Not Write Any Custom Verilog

endmodule

rhdl_ExternalSignalInterface.v

module rhdl_ExternalSignalInterface
   (start_sink,
   READ_ADDR_I,
   out_mux_DATA_0_I_i,
   RESET_I,
   CLK_I,
   WRITE_ADDR_I,
   DATA_I,
   WRITE_I,
   DATA_O,
   I_ENABLE_1_o,
   io_test_source_1_o);

      input start_sink;
      input [13:0] READ_ADDR_I;
      input [4:0] out_mux_DATA_0_I_i;
      input RESET_I;
      input CLK_I;
      input [12:0] WRITE_ADDR_I;
      input [12:0] DATA_I;
      input WRITE_I;

      output [15:0] DATA_O;
      output I_ENABLE_1_o;
      output [7:0] io_test_source_1_o;

      wire sel_I_ENABLE_1;
      wire [10:0] out_mux_DATA_0_I_c;
      wire [7:0] DATA_1_I_extra_bits_DATA_O;
      wire write_buff_in_CLK_O;
      wire sel_io_test_source_0;

      wire [15:0] out_mux_DATA_0_I;
      wire START_I;



//*****  Redundant Wiring  *****

         assign out_mux_DATA_0_I = {out_mux_DATA_0_I_c,out_mux_DATA_0_I_i};
         assign START_I = {start_sink};

//*****  Subcomponet Declarations  *****



//*****  Skipping   with 0 outputs.  *****


rhdl_Decoder addr_decode (
      .In({WRITE_ADDR_I[1:0]}),
      .Out({sel_I_ENABLE_1,sel_io_test_source_0})
   );

rhdl_ClockSink write_buff_in (
      .CLK_I({WRITE_I}),
      .CLK_O({write_buff_in_CLK_O})
   );

rhdl_RegisterComponent2 in_reg_I_ENABLE_1 (
      .ENABLE_I({sel_I_ENABLE_1}),
      .CLK_I({write_buff_in_CLK_O}),
      .REG_I({DATA_I[0]}),
      .REG_O({I_ENABLE_1_o})
   );

rhdl_Constant8 DATA_1_I_extra_bits (
      .DATA_O({DATA_1_I_extra_bits_DATA_O})
   );


//*****  Skipping   with 0 outputs.  *****


rhdl_Mux out_mux (
      .SEL_I({READ_ADDR_I[0]}),
      .DATA_0_I({out_mux_DATA_0_I_c,out_mux_DATA_0_I_i}),
      .DATA_1_I({DATA_1_I_extra_bits_DATA_O,io_test_source_1_o}),
      .DATA_O({DATA_O})
   );

rhdl_RegisterComponent3 in_reg_io_test_source_1 (
      .ENABLE_I({sel_io_test_source_0}),
      .CLK_I({write_buff_in_CLK_O}),
      .REG_I({DATA_I[7:0]}),
      .REG_O({io_test_source_1_o})
   );


//*****  Skipping   with 0 outputs.  *****


rhdl_Constant9 DATA_0_I_extra_bits (
      .DATA_O({out_mux_DATA_0_I_c})
   );

// Component Did Not Write Any Custom Verilog

endmodule

rhdl_TopLevelComponent

module rhdl_TopLevelComponent
   (main_reset,
   io_test,
   counter_ENABLE_I,
   main_clock_CLK_I,
   counter_COUNT_O);

      input main_reset;
      input [7:0] io_test;
      input counter_ENABLE_I;
      input main_clock_CLK_I;

      output [4:0] counter_COUNT_O;

      wire counter_count_reg_CLK_I;




//*****  Redundant Wiring  *****


//*****  Subcomponet Declarations  *****


rhdl_UpCounter counter (
      .RESET_I({main_reset}),
      .ENABLE_I({counter_ENABLE_I}),
      .count_reg_CLK_I({counter_count_reg_CLK_I}),
      .COUNT_O({counter_COUNT_O})
   );

rhdl_MainClock main_clock (
      .CLK_I({main_clock_CLK_I}),
      .CLK_O({counter_count_reg_CLK_I})
   );

// Component Did Not Write Any Custom Verilog

endmodule

Summary

The intention of this topic is to aid somebody who desires to reverse engineer the communincation between the Software and Hardware in Rapid HDL.

If you desire to create your own interface to some FPGA other than the Pico E-12, this is a recommendation...
  • Establish a communication channel between a .Net program and your FPGA that can be abstracted as a 16 bit data bus having at least 8 addresses.
  • Map rhdlpicoperipherals.v to this data channel
    • This may require reworking the SDRAM memory access
  • Expose your .Net software as a class library with functions that are identical to PicoInvoke.cs

Last edited Sep 11, 2009 at 3:26 AM by allen248, version 3

Comments

No comments yet.