Lattice 自定义IP业务逻辑核

概述

Lattice IP Packager 是一个图形工具,用来把 IP 核的所有代码、配置和资源整理成标准 IP 包,让 Radiant 和 Propel 能直接调用使用。本文档的目的是介绍 Lattice IP Packager 2025.2,帮助你快速上手,并创建一个自定义 IP 模块的软件包。

操作教程

打开软件Lattice Propel Builder 2025.2.1里面点击Tools->IP Packager,如下所示

打开文件夹File->Open IP Directory,选择工程文件存放的文件夹,然后界面如下:

  • Meta Data
  • Design File
  • Doc

上面三个为必配置项,

  • Test Bench
  • Constraint
  • Misc

上面三个为可配置项,配置Basic Info信息如下:

新增一个自定义的GPIO输出的例子

新增gpio的位宽Param配置参数如下:

加载自定义的参数,verilog的接口如下:

c 复制代码
module apb_gpio #(
    parameter DATA_WIDTH = 32   // GPIO width: 1~32
)(
    // ==========================test===========================
    // System signals
    // =====================================================
    input  wire                    clk_i,
    input  wire                    resetn_i,

    // =====================================================
    // APB interface (FIXED 32-bit standard)
    // =====================================================
    input  wire [31:0]             PADDR,
    input  wire                    PSEL,
    input  wire                    PENABLE,
    input  wire                    PWRITE,
    input  wire [31:0]             PWDATA,
    output reg  [31:0]             PRDATA,
    output wire                    PREADY,
    output wire                    PSLVERR,

    // =====================================================
    // Interrupt
    // =====================================================
    output reg                     IRQ,

    // =====================================================
    // GPIO interface (VARIABLE WIDTH)
    // =====================================================
    inout  wire [DATA_WIDTH-1:0]   gpio_io
);

然后配置gpio_io的属性参数,这样新增IP核的时候,自动根据配置的宏参数,生成对应的代码:

然后新增APB的接口,和中断的管脚:

在上传Design Files文件的时候,注意要修改文件名重新上传才能生效,否则生成的rtl文件里面的.v文件不变

化:

代码如下:

c 复制代码
module apb_gpio #(
    parameter DATA_WIDTH = 8
)(
    input  wire                    clk_i,
    input  wire                    resetn_i,
    input  wire [31:0]             PADDR,
    input  wire                    PSEL,
    input  wire                    PENABLE,
    input  wire                    PWRITE,
    input  wire [31:0]             PWDATA,
    output reg  [31:0]             PRDATA,
    output wire                    PREADY,
    output wire                    PSLVERR,

    output reg                     IRQ,
    inout  wire [DATA_WIDTH-1:0]   gpio_io
);
    // =====================================================
    // Registers
    // =====================================================
    reg [DATA_WIDTH-1:0] gpio_out_reg;
    reg [DATA_WIDTH-1:0] gpio_dir_reg;
    reg [DATA_WIDTH-1:0] reg_int_en;
    reg [DATA_WIDTH-1:0] reg_int_type; // 1=edge, 0=level
    reg [DATA_WIDTH-1:0] reg_int_pol;  // 1=rising/high, 0=falling/low
    reg [DATA_WIDTH-1:0] reg_int_sts;
    wire [DATA_WIDTH-1:0] gpio_in;
    // =====================================================
    // GPIO tri-state (FIXED: per-bit control)
    // =====================================================
    genvar i;
    generate
        for (i = 0; i < DATA_WIDTH; i = i + 1) begin
            assign gpio_io[i] = gpio_dir_reg[i] ? gpio_out_reg[i] : 1'bz;
            assign gpio_in[i] = gpio_io[i];
        end
    endgenerate
    // =====================================================
    // Synchronizer (2FF)
    // =====================================================
    reg [DATA_WIDTH-1:0] sync0, sync1, sync2;
    always @(posedge clk_i or negedge resetn_i) begin
        if (!resetn_i) begin
            sync0 <= 0;
            sync1 <= 0;
            sync2 <= 0;
        end else begin
            sync0 <= gpio_in;
            sync1 <= sync0;
            sync2 <= sync1;
        end
    end
    wire [DATA_WIDTH-1:0] gpio_curr = sync1;
    wire [DATA_WIDTH-1:0] gpio_prev = sync2;
    // =====================================================
    // Edge detect
    // =====================================================
    wire [DATA_WIDTH-1:0] rising_edge  =  gpio_curr & ~gpio_prev;
    wire [DATA_WIDTH-1:0] falling_edge = ~gpio_curr &  gpio_prev;

    wire [DATA_WIDTH-1:0] edge_event =
        (reg_int_pol ? rising_edge : falling_edge);
    // =====================================================
    // Level detect
    // =====================================================
    wire [DATA_WIDTH-1:0] level_event =
        (reg_int_pol ? gpio_curr : ~gpio_curr);
    // =====================================================
    // Select mode
    // =====================================================
    wire [DATA_WIDTH-1:0] int_event =
        reg_int_type ? edge_event : level_event;

    wire [DATA_WIDTH-1:0] int_active = int_event & reg_int_en;
    // =====================================================
    // APB ready
    // =====================================================
    assign PREADY  = 1'b1;
    assign PSLVERR = 1'b0;
    wire apb_write = PSEL && PENABLE && PWRITE;
    wire apb_read  = PSEL && !PWRITE;
    // =====================================================
    // Write registers
    // =====================================================
    always @(posedge clk_i or negedge resetn_i) begin
        if (!resetn_i) begin
            gpio_out_reg <= 0;
            gpio_dir_reg <= 0;
            reg_int_en   <= 0;
            reg_int_type <= 0;
            reg_int_pol  <= 0;
            reg_int_sts  <= 0;
        end else begin
            if (apb_write) begin
                case (PADDR[7:0])
                    8'h00: gpio_out_reg <= PWDATA[DATA_WIDTH-1:0];
                    8'h04: gpio_dir_reg <= PWDATA[DATA_WIDTH-1:0];
                    8'h08: reg_int_en   <= PWDATA[DATA_WIDTH-1:0];
                    8'h0C: reg_int_type <= PWDATA[DATA_WIDTH-1:0];
                    8'h10: reg_int_pol  <= PWDATA[DATA_WIDTH-1:0];

                    // W1C interrupt status
                    8'h14: reg_int_sts <= reg_int_sts & ~PWDATA[DATA_WIDTH-1:0];

                    default: ;
                endcase
            end
            // latch interrupt
            reg_int_sts <= reg_int_sts | int_active;
        end
    end
    // =====================================================
    // Read registers
    // =====================================================
    always @(*) begin
        case (PADDR[7:0])
            8'h00: PRDATA = {{(32-DATA_WIDTH){1'b0}}, gpio_out_reg};
            8'h04: PRDATA = {{(32-DATA_WIDTH){1'b0}}, gpio_dir_reg};
            8'h08: PRDATA = {{(32-DATA_WIDTH){1'b0}}, gpio_in};
            8'h0C: PRDATA = {{(32-DATA_WIDTH){1'b0}}, reg_int_type};
            8'h10: PRDATA = {{(32-DATA_WIDTH){1'b0}}, reg_int_pol};
            8'h14: PRDATA = {{(32-DATA_WIDTH){1'b0}}, reg_int_sts};
            default: PRDATA = 32'h0;
        endcase
    end
    // =====================================================
    // IRQ output
    // =====================================================
    always @(posedge clk_i or negedge resetn_i) begin
        if (!resetn_i)
            IRQ <= 1'b0;
        else
            IRQ <= |reg_int_sts;
    end
endmodule

在下面的界面新增驱动文件:

驱动文件如下:

c 复制代码
#include "gpio_drv.h"
/* =========================
 * init
 * ========================= */
void gpio_init(apb_gpio_t *gpio, uint32_t base_addr, uint32_t width)
{
    gpio->base  = (uint32_t *)base_addr;
    gpio->width = width;

    gpio->base[GPIO_REG_OUT/4] = 0;
    gpio->base[GPIO_REG_DIR/4] = 0;
}
/* =========================
 * GPIO output
 * ========================= */
void gpio_write(apb_gpio_t *gpio, uint32_t value)
{
    gpio->base[GPIO_REG_OUT/4] = value;
}
uint32_t gpio_read_out(apb_gpio_t *gpio)
{
    return gpio->base[GPIO_REG_OUT/4];
}
/* =========================
 * GPIO input
 * ========================= */
uint32_t gpio_read_in(apb_gpio_t *gpio)
{
    return gpio->base[GPIO_REG_IN/4];
}
/* =========================
 * direction
 * 1 = output
 * 0 = input
 * ========================= */
void gpio_set_dir(apb_gpio_t *gpio, uint32_t dir)
{
    gpio->base[GPIO_REG_DIR/4] = dir;
}
/* =========================
 * interrupt enable
 * ========================= */
void gpio_int_enable(apb_gpio_t *gpio, uint32_t mask)
{
    gpio->base[GPIO_REG_INT_EN/4] |= mask;
}
void gpio_int_disable(apb_gpio_t *gpio, uint32_t mask)
{
    gpio->base[GPIO_REG_INT_EN/4] &= ~mask;
}
/* =========================
 * interrupt mode
 * ========================= */
void gpio_int_set_type(apb_gpio_t *gpio, uint32_t mask)
{
    gpio->base[GPIO_REG_INT_TYPE/4] |= mask; // edge
}
void gpio_int_set_level(apb_gpio_t *gpio, uint32_t mask)
{
    gpio->base[GPIO_REG_INT_TYPE/4] &= ~mask; // level
}
/* rising/falling */
void gpio_int_set_rising(apb_gpio_t *gpio, uint32_t mask)
{
    gpio->base[GPIO_REG_INT_POL/4] |= mask;
}
void gpio_int_set_falling(apb_gpio_t *gpio, uint32_t mask)
{
    gpio->base[GPIO_REG_INT_POL/4] &= ~mask;
}
/* =========================
 * status
 * ========================= */
uint32_t gpio_int_get_status(apb_gpio_t *gpio)
{
    return gpio->base[GPIO_REG_INT_STS/4];
}
/* =========================
 * clear (W1C)
 * ========================= */
void gpio_int_clear(apb_gpio_t *gpio, uint32_t mask)
{
    gpio->base[GPIO_REG_INT_STS/4] = mask;
}

驱动头文件:

c 复制代码
#ifndef _APB_GPIO_DRV_H_
#define _APB_GPIO_DRV_H_
#include <stdint.h>
/* =========================
 * Register Offsets
 * ========================= */
#define GPIO_REG_OUT        0x00
#define GPIO_REG_DIR        0x04
#define GPIO_REG_IN         0x08
#define GPIO_REG_INT_EN     0x0C
#define GPIO_REG_INT_TYPE   0x10
#define GPIO_REG_INT_POL    0x14
#define GPIO_REG_INT_STS    0x18
/* =========================
 * GPIO Driver Handle
 * ========================= */
typedef struct {
    volatile uint32_t *base;
    uint32_t width;
} apb_gpio_t;
/* =========================
 * Basic IO
 * ========================= */
void gpio_init(apb_gpio_t *gpio, uint32_t base_addr, uint32_t width);
/* output */
void gpio_write(apb_gpio_t *gpio, uint32_t value);
uint32_t gpio_read_out(apb_gpio_t *gpio);
/* input */
uint32_t gpio_read_in(apb_gpio_t *gpio);
/* direction */
void gpio_set_dir(apb_gpio_t *gpio, uint32_t dir);
/* =========================
 * Interrupt control
 * ========================= */
void gpio_int_enable(apb_gpio_t *gpio, uint32_t mask);
void gpio_int_disable(apb_gpio_t *gpio, uint32_t mask);
void gpio_int_set_type(apb_gpio_t *gpio, uint32_t mask);   // 1=edge
void gpio_int_set_level(apb_gpio_t *gpio, uint32_t mask);  // 0=level
void gpio_int_set_rising(apb_gpio_t *gpio, uint32_t mask);
void gpio_int_set_falling(apb_gpio_t *gpio, uint32_t mask);
/* status */
uint32_t gpio_int_get_status(apb_gpio_t *gpio);
/* clear (W1C) */
void gpio_int_clear(apb_gpio_t *gpio, uint32_t mask);
#endif

配置完成后,点击IP Preview按钮后,出现如下界面,测试修改GPIO width的值,发现左侧的Diagram u_gpio_inst 的预览gpio接口随之变化如下:

随之弹出文件夹,重点关注rtl文件夹里面的u_gpio_inst.v文件是否是自己上传的.v文件。

然后在Lattice Propel Builder软件里New Soc Design里面新建工程,具体的细节参考lattice propel的使用例子前面的这篇文章,主要注意Schematic电路配置如下:

注意锁住Address的参数:

然后从Propel 软件里打开Run Radiant ,执行编译,注意物理管教的限制文件,例如uart管脚核LED的四个输出管脚如下:

具体的下载方法见博文Lattice Radiant第一个跑马灯例子lattice propel的使用例子,下面开始编写应用代码跑马灯如下:

c 复制代码
#include "hal.h"
#include "utils.h"
#include <stdio.h>
#include "sys_platform.h"
#include <riscv_errors.h>
#include "platform_init.h"
#include "device_info.h"
#include "terminal.h"
#include "gpio_drv.h"
#if REG_TEST_ENABLE
#include "reg_test.h"
#endif
#ifdef LED_GPIO_INST_BASE_ADDR
#include "gpio.h"
#define LED_COUNT 8
struct gpio_instance led_gpio_inst;
#else
#define LED_COUNT 8
#endif

static int bsp_init(void)
{
	int ret = 0;

	ret = terminal_init(IRQ_BAREMETAL_DEFAULT);

#if CPU_INST_0_PIC_ENABLE
	// initialize PIC. for MC/SM/NANO
	pic_init(CPU_INST_0_PICTIMER_START_ADDR);
#endif

#if CPU_INST_0_PLIC_EN
	// initialize PLIC. for RX
	plic_init();
#endif

#if CPU_INST_0_PLIC_EN || CPU_INST_0_CLINT_EN
	trap_init();
#endif

#if REG_TEST_ENABLE
	/* Used for register access test, If not sure how to use, disable it. */
	reg_test_assert(mem_access_test());
	printf("\nmem_access_test success!\n\n");
#endif
#ifdef LED_GPIO_INST_BASE_ADDR
	// initialize LED GPIO
	led_gpio_inst.instance_name = LED_GPIO_INST_NAME;
	gpio_init(&led_gpio_inst, LED_GPIO_INST_BASE_ADDR, LED_GPIO_INST_LINES_NUM, LED_GPIO_INST_GPIO_DIRS);
#endif

#ifdef RISCV_RX_DRV_VER
	/*If driver support, set global interrupt-enable bit to 1.*/
#if CPU_INST_0_PLIC_EN || CPU_INST_0_CLINT_EN
	plic_enable_global_interrupts(1);
#endif
#endif
	return ret;
}
int main(void)
{
	static uint8_t idx = 0;
	static uint8_t pin_state = 0xFF;
	apb_gpio_t usr_gpio;
	memset(&usr_gpio, 0, sizeof(usr_gpio));
	bsp_init();
	printf("Started!\r\nHello RISC-V world!\r\n");
	gpio_init(&usr_gpio, USR_INST_BASE_ADDR, 4);
	gpio_set_dir(&usr_gpio, 0x0f);
	while (true)
	{
		idx++;
		gpio_write(&usr_gpio, idx&0x0f);
		if (RTL_SIM)
		{
			delay(1);
		}
		else
		{
			delay(10);
		}
		gpio_write(&usr_gpio, idx&0x0f);
		if (RTL_SIM)
		{
			delay(1);
		}
		else
		{
			delay(10);
		}
	}
	return 0;
}

工程文件如下:

下载到下位机里,就可以观察到下位机的led灯按照不同的频率闪烁了

相关推荐
AI的探索之旅2 小时前
从 Ubuntu 14.04 到 24.04:TI AM335x 开发环境完整迁移与 Agent 接管方案
linux·数据库·嵌入式硬件·ubuntu·postgresql
ACP广源盛139246256732 小时前
GSV6155 @ACP#工业车规 DP1.4 重定时器 Retimer
大数据·人工智能·分布式·嵌入式硬件
sramdram2 小时前
低功耗MCU芯片电机控制专用
单片机·嵌入式硬件·mcu·低功耗mcu·mcu芯片
ACP广源盛139246256733 小时前
GSV2221@ACP# DP1.4 MST 多流显示转换芯片国产工业多屏
大数据·人工智能·分布式·嵌入式硬件·spark
ACP广源盛139246256734 小时前
GSV2231@ACP# 增强型 8K MST 多屏扩展芯片
大数据·人工智能·分布式·嵌入式硬件
FPGA小徐4 小时前
FPGA FIFO一篇完整解释
fpga开发
I'm a winner12 小时前
【IP核】 Xilinx FPGA LVDS 高速接口,含验证工程与板级测试用例
tcp/ip·fpga开发·测试用例
I'm a winner14 小时前
基于Xilinx FPGA的LVDS高速串行通信系统 - 完整源码解决方案(一)(文末附源码)
fpga开发
国科安芯15 小时前
航天器多路并联大功率电源系统设计与ASP4644均流特性分析
单片机·嵌入式硬件·fpga开发·安全性测试