AM3359 中利用GPMC控制器驱动FIFO

2023-11-11 08:40

本文主要是介绍AM3359 中利用GPMC控制器驱动FIFO,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

开发板:英蓓特科技有限公司的Mini8600B

处理器:TI公司的AM3359

操作系统:Linux3.2.0

FIFO芯片:IDT7205L20

硬件连接图:

 

 

一、GPMC module 简介

1、  GPMC简介

GPMC用于控制external memory的读写访问,GPMC的地址为0x0000 0000到0x1fff ff ff,最大支持512MB的片外存储器访问;GPMC寄存器的基地址为0x5000 0000,用于配置GPMC模块。GPMC能提供最大100MHz的外部时钟频率(gpmc_clk)。

支持的设备或存储器:

       NAND flash与NOR flash结构的device/memory:

       (1)8bit与16bit的同步/异步读写device/memory

       (2)16bit地址数据复用的NOR flash(pSRAM)

       (3)8bit与16bit的NAND flash

(4)其他支持的设备:使用NOR interface protocol进行通信,

Technical Reference Manual上有这么一句:Other supported device type interact with the GPMC through the NOR interface protocol.

2NOR Interface Protocol

 

NOR flash architecture, introduced in 1988, is a flash technology. Unlike NAND, which is a sequential access device, NOR is directly addressable; i.e., it is designed to be a random access device. NOR is best suited to devices used to store and run code or firmware, usually in small capacities. While NOR has fast read capabilities it has slow write and erase functions compared to NAND architecture.

       3、GPMC编程模式

如上图所示,使用GPMC驱动外设时,要先对其进行初始化和配置。GPMC支持软件复位操作,通过GPMC_SYSCONFIG寄存器的配置来实现。接下来配置GPMC,NOR类型的device的配置要比NAND简单得多,主要配置GPMC_CONFIG1_i到GPMC_CONFIG7_i这七个寄存器,这些寄存器包含了memory类型(NAND、NOR)的配置,片选配置,时序配置等信息,关于片选配置,下面会详细描述。需要注意的是,在配置GPMC这七个片选寄存器之前,要先使GPMC_CONFIG7_i的CSVALID disabled,Technical Reference Manual上有这么一句:Chip-select configuration (base and mask address or any protocol and timing settings) must be performed      while the associated chip-select is disabled through the GPMC_CONFIG7_i[6] CSVALID bit. In addition, a chip-select configuration can only be disabled if there is no ongoing access to that chip-select. 片选寄存器配置完之后应当enable CSVALID。

片寻配置(chip-select configuration):GPMC最大支持512MB的片外寻址,被分成8片,每片的大小可选以下数值:16M、32M、64M、128M、256M,但8片大小的总和不能大于512MB。可通过GPMC_CONFIG7_i来配置片寻基址、片寻大小等,片寻基址必须是2的n次方,其他基址应该避免,否则会产生hole。其中CS0~CS6是可配置的,而CS7不是外部引脚(not pin out),配置了也没用。我研究了Linux3.2.0中NAND驱动的源码,发现NAND使用的是CS0,用户可从CS1~CS6中选择一个来设计自己的驱动。

二、FIFO芯片IDT7205L20简介

IDT7205L20是Integrated Device Technology,Inc.公司的一款8K*9bit的FIFO芯片,

异步读写,没有时钟引脚,没有地址引脚。通过WE和OE来控制读写,以读为例,OE变为低电平(enable电平)时,数据送到输出端口,等待CPU取走,CPU取走数据后,OE变为高电平(disable 电平),这样就完成了一个读周期。FIFO内部有一个读指针和写指针,复位后读写指针指向同一个位置。每往FIFO写一个数据时,写指针加一,每从FIFO读一个数据时,读指针加一,当读写指针相等时,FIFO空标志EF变为低电平,此时数据输出线呈高阻态,不能再读数据。当往FIFO写满8K个数据后,满标志FF变为低电平,数据输入线呈高阻态,不能再写数据。

在GPMC_CONFIG1_i中应当配置成异步读写,并且是非地址/数据复用模式,采用NOR接口协议。此外还需配置GPMC_CONFIG4_i的 WEONTIME和WEOFFTIME,OEONTIME和OEOFFTIM,根据你的FIFO datasheet来配置时间,一般是几十个纳秒级的,然后配置GPMC_CONFIG5_i和GPMC_CONFIG6_i的total read time和total write time。这些工作做完之后,FIFO与GPMC应该可以通信了。

三、 配置GPMC引脚

在以下文件Linux-3.2.0-psp04.06.00.08.sdk/arch/arm/mach-omap2/board-am335xevm.c

中配置:

1、添加如下引脚配置代码

/*pin mux for FIFO writing*/

static struct pinmux_config fifo_pin_mux[] = {

       {"gpmc_ad0.gpmc_ad0",        OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad1.gpmc_ad1",        OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad2.gpmc_ad2",        OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad3.gpmc_ad3",        OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad4.gpmc_ad4",        OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad5.gpmc_ad5",        OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad6.gpmc_ad6",        OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad7.gpmc_ad7",        OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad8.gpmc_ad8",        OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad9.gpmc_ad9",        OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad10.gpmc_ad10",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad11.gpmc_ad11",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad12.gpmc_ad12",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad13.gpmc_ad13",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad14.gpmc_ad14",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

       {"gpmc_ad15.gpmc_ad15",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

 

       {"gpmc_csn1.gpio1_30",        OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

       {"gpmc_csn2.gpio1_31",        OMAP_MUX_MODE7 | AM33XX_PIN_INPUT},

       {"gpmc_csn3.gpio2_0",   OMAP_MUX_MODE7 | AM33XX_PIN_INPUT},

       {"gpmc_clk.gpio2_1",      OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

       {"lcd_vsync.gpio2_22",   OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

       {"lcd_pclk.gpio2_24",      OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

       {"gpmc_wen.gpmc_wen",     OMAP_MUX_MODE0 | AM33XX_PULL_DISA},

       {"gpmc_ben1.gpmc_dir",    OMAP_MUX_MODE4 | AM33XX_PULL_DISA},

 

       {NULL, 0},

};

2、定义GPMC device,这个gpmc_device的内容可根据驱动的实际情况而定,其目的是作为调用GPMC初始化函数omap_init_gpmc()的参数。

       struct omap_fifo_platform_data {

              int                  cs;

              struct gpmc_timings      *gpmc_t;

              int                  gpmc_irq;

              unsigned long         phys_base;

              int                  devsize;

              bool               elm_used;

};

 

static struct gpmc_timings fifo_timings = {

       .sync_clk = 0,

 

       .cs_on = 0,     /* Assertion time */

       .cs_rd_off = 82,/* Read deassertion time */

       .cs_wr_off = 82,/* Write deassertion time */

 

       .we_on = 40,  /* WE assertion time */

       .we_off  = 40,      /* WE deassertion time */

 

       .oe_on = 40,   /* OE assertion time */

       .oe_off = 40,  /* OE deassertion time */

      

       .access = 64,  /* Start-cycle to first data valid delay */

       .rd_cycle = 82,      /* Total read cycle time */

       .wr_cycle = 82,     /* Total write cycle time */

};

 

static struct omap_fifo_platform_data fifo_data = {

       .cs = 1,

       .gpmc_t = &fifo_timings,

       .devsize = 16,

       .elm_used = true,

};

 

static struct gpmc_devices_info gpmc_device[2] = {

       { .pdata = &fifo_data, .flag = GPMC_DEVICE_NOR },

       { NULL, 0 }, /* reserved */

};

3、添加初始化函数fifo_init(),调用setup_pin_mux()使引脚配置生效,调用omap_init_gpmc()与omap_init_elm()初始化GPMC,这一步是必须的,否则GPMC相关的函数不能使用。

static void fifo_init(int evm_id, int profile)

{

       setup_pin_mux(fifo_pin_mux);

       omap_init_gpmc(gpmc_device, sizeof(gpmc_device));

       omap_init_elm();

       return;

}

3、在结构体evm_dev_cfg sbc8600_dev_cfg[]中添加     

{fifo_init, DEV_ON_BASEBOARD, PROFILE_ALL},

系统初始化的时候会调用下面的函数:

_configure_device(EVM_SK, sbc8600_dev_cfg, PROFILE_NONE);

4、注意事项

引脚配置不应有冲突,即不应有两个或以上的驱动配置相同的引脚,同一个引脚不应被配置两次或多次不同的数值。

5、编译内核,生成uImage文件,下载到开发板上。

四、驱动设计

1、除一般驱动所要的头文件外,还需包含的头文件

#include

#include

#include

#include

#include

2、定义片选号、GPIO引脚等

/* GPMC chip select */

#define GPMC_CS         4

#define NONE_MUXADDATA  ~(0x03 << 8)

 

/* GPIO pin */

#define GPIO_TO_PIN(bank, gpio)     ((32 * (bank)) + (gpio))

#define GPMC_CSN1_RESET  GPIO_TO_PIN(1, 30) /*FIFO reset*/

 

#define GPMC_DEVICE_NAME "mygpmc"

#define GPMC_DRIVER_NAME "gpmc_write"

 

#define FIFO_SIZE SZ_8K

#define BUF_SIZE 1024

3、  配置时序信息

static struct gpmc_timings fifo_timings = {/*GPMC timing configurations*/

    .sync_clk = 0,

 

    .cs_on = 0,             /* Assertion time */

    .cs_rd_off = 82,      /* Read deassertion time */

    .cs_wr_off = 82,     /* Write deassertion time */

 

    .we_on = 40,   /* WE assertion time */

    .we_off  = 40,       /* WE deassertion time */

 

    .oe_on = 40,    /* OE assertion time */

    .oe_off = 40,   /* OE deassertion time */

 

    .access = 50,   /* Start-cycle to first data valid delay */

    .rd_cycle = 82,       /* Total read cycle time */

    .wr_cycle = 82,      /* Total write cycle time */

};

4、  配置GPMC寄存器

/*Chip-select configuration (base and mask address or any protocol and timing settings) must be performed while the associated chip-select is disabled through the GPMC_CONFIG7_i[6] CSVALID bit. In addition, a chip-select configuration can only be disabled if there is no ongoing access to that chip-select.*/

    val = gpmc_cs_read_reg(GPMC_CS, GPMC_CS_CONFIG7);

    val &= ~GPMC_CONFIG7_CSVALID; /*disable chip-select*/

    gpmc_cs_write_reg(GPMC_CS, GPMC_CS_CONFIG7, val);

 

    gpmc_cs_configure(GPMC_CS, GPMC_SET_IRQ_STATUS, 0); /*set irq status*/

gpmc_cs_configure(GPMC_CS, GPMC_ENABLE_IRQ, 0); /*disable irqs*/

 

    gpmc_cs_write_reg(GPMC_CS,GPMC_CS_CONFIG1, GPMC_CONFIG1_READTYPE_ASYNC  | /*set read type Asynchronous*/

        GPMC_CONFIG1_WRITETYPE_ASYNC  | /*set write type Asynchronous*/

        GPMC_CONFIG1_DEVICESIZE_16      | /*set device size 16bit*/

        GPMC_CONFIG1_DEVICETYPE_NOR   | /*set device type NOR*/

GPMC_CONFIG1_FCLK_DIV3);    /*GPMC_CLK frequency=GPMC_FCLK frequency/3*/

    val = gpmc_cs_read_reg(GPMC_CS, GPMC_CS_CONFIG1);

    val &= NONE_MUXADDATA; /*Non-multiplexed attached device*/

    gpmc_cs_write_reg(GPMC_CS, GPMC_CS_CONFIG1, val);

 

ret = gpmc_cs_set_timings(GPMC_CS, &fifo_timings); /*set timing parameters*/

 

这段代码编译时可能会提示gpmc_cs_write_reg、 gpmc_cs_read_reg、 gpmc_cs_set_timings这几个函数undefined,这时需要在mach-omap2/gpmc.c文件中找到这些函数,并在函数定义的末尾添加EXPORT_SYMBOL(gpmc_cs_write_reg);

EXPORT_SYMBOL(gpmc_cs_read_reg); EXPORT_SYMBOL(gpmc_cs_set_timings);

重新编译内核,下载到开发板上。

5、  申请gpmc片选空间并用ioremap函数映射为内核虚拟地址

    /***********为FIFO申请GPMC chip-select空间**********/

    if (gpmc_cs_request(GPMC_CS, FIFO_SIZE, &mem_base)<0)

    {

        printk(KERN_ERR "gpmc_cs_request failed.\n");

        return -1;

    }

 

    /***********为FIFO申请虚拟内存空间**********/

    if (!request_mem_region(mem_base, FIFO_SIZE, GPMC_DRIVER_NAME))

    {

        printk(KERN_ERR "request_mem_region failed.\n");

        return -1;

    }

    /******物理地址映射为虚拟地址*****/

fifo_base = ioremap (mem_base, FIFO_SIZE);

6、  完成以上步骤后,就可以对fifo进行读写了

writew(gpmc_dev.user_buff[i], fifo_base);

gpmc_dev.user_buff[i] = readw(fifo_base);

FIFO是一个none ramdom access的器件,每次的读写操作都基于同一个地址。

7、  中断

GPMC有中断但是内部中断,只有wait0与wait1引脚是外部中断,而wait引脚通常连接外设的ready引脚,所以采用GPIO引脚来产生中断:

GPIO引脚号转换为中断号: irq_num = gpio_to_irq(gpio_pin);

设置中断出发方式为下降沿触发

irq_set_irq_type(irq_num, IRQF_TRIGGER_FALLING);

申请中断:

          request_irq(irq_num, irq_proc, IRQF_DISABLED, GPIO_DEVICE_NAME, NULL);

中断处理函数:

static irqreturn_t irq_proc(int irq, void *dev_id)

{

//    your code

                  return IRQ_HANDLED;

}

中断类型IRQF_DISABLED表示此中断处理函数调用时,屏蔽所有中断,更多信息可在头文件 与中找到。

8、  释放GPMC、 GPIO资源

release_mem_region(mem_base, FIFO_SIZE);

gpmc_cs_free(GPMC_CS);

iounmap(fifo_base);

gpio_free(gpio_pin);

9、释放中断资源

free_irq(irq_num, NULL);

10、驱动源程序

见附件

五、Makefile

       ARCH=arm

CROSS_COMPILE=/opt/arm-2009q1/bin/arm-none-linux-gnueabi-

KERNELDIR := /home/linfan/linux-3.2.0-for-write

obj-m      := gpmc_write.o

PWD       := $(shell pwd)

default:

$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules

app:

       arm-none-linux-gnueabi-gcc test_write.c -o write

clean:

       rm -rf *.o *.ko *.mod.c *.symvers *.order

六、用户空间应用程序

       见附件

七、总结

AM3359的驱动设计要在board-am335xevm.c中定义设备和配置引脚,然后再编译内核,生成映像文件,颇有不便之处。我在编写这个驱动时,大部分时间花在修改内核源码及编译内核之上。

GPMC驱动设计的关键是要正确配置相关的寄存器,否则得不到想要的运行结果。对于NOR类型的器件,只需配置GPMC_CONFIG1~ GPMC_CONFIG7这几个寄存器就可以了;对于NAND类型的器件,还需配置prefetch、ECC engine等的内容。我在写这个驱动时,由于在GPMC_CONFIG1中配置了同步读写,以及一些时序上的不正确配置,一直不能往/从FIFO读写数据,后来通过FIFO datasheet的研究,才发现并解决这个问题的。

Linux3.2.0内核源码中有gpmc.h与 gpmc.c文件,里面的函数对GPMC的支持还是比较全面的,但要使用这些函数,还要先调用omap_init_gpmc()与omap_init_elm()初始化GPMC device,否则无法调用insmod加载驱动模块。有些函数明明在gpmc.c文件中有定义,但编译时却提示undefined,对于这个问题,我的解决办法是,对于非static函数,调用EXPORT_SYMBOL()来导出这些函数。编译内核时,会在module.symvers文件中生成相应的symbol。当编译驱动模块时,编译器自动进入你指定的Linux内核源码目录,而你的驱动中的系统调用则由编译器在module.symvers文件中查找,如果找不到,则会提示undefined。

我曾试图自己编写配置GPMC寄存器的函数,方法是根据technical reference manual里的memory map找到GPMC寄存器的地址,通过ioremap函数转换为内核虚拟地址,然后调用writel(),readl()之类的函数将配置data写进寄存器中。但编写起来所遇到的问题比解决的问题还多,要考虑的方面很多,是一个浩大的工程。对于编写一个小程序测试一下还是容易的,当你的工程比较大的时候,这个工作就显得困难了,而且代码的可读性与可移植性都比较差。所以还是使用系统自带的函数来操作好,毕竟是经过原厂编译并测试通过的,可靠性稳定性都比较高。

这篇关于AM3359 中利用GPMC控制器驱动FIFO的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/388866

相关文章

LabVIEW FIFO详解

在LabVIEW的FPGA开发中,FIFO(先入先出队列)是常用的数据传输机制。通过配置FIFO的属性,工程师可以在FPGA和主机之间,或不同FPGA VIs之间进行高效的数据传输。根据具体需求,FIFO有多种类型与实现方式,包括目标范围内FIFO(Target-Scoped)、DMA FIFO以及点对点流(Peer-to-Peer)。 FIFO类型 **目标范围FIFO(Target-Sc

LabVIEW开发FPGA方法与FIFO数据丢失处理

开发基于NI 7975R FPGA的系统涉及一系列流程,包括驱动安装、LabVIEW项目设置、开发调试、编译和与Windows系统的通信。重点在于FIFO的正确配置,避免数据丢失是关键环节之一,尤其是在使用高速数据流传输时。以下将详细介绍这些过程,并重点讨论FIFO数据丢失的原因与解决方案。 FPGA开发流程 驱动安装与工具准备:开发FPGA需要安装LabVIEW FPGA模块以及N

页面置换算 - FIFO、LFU、LRU

缓存算法(页面置换算法)-FIFO、LFU、LRU   在前一篇文章中通过leetcode的一道题目了解了LRU算法的具体设计思路,下面继续来探讨一下另外两种常见的Cache算法:FIFO、LFU 1.FIFO算法   FIFO(First in First out),先进先出。其实在操作系统的设计理念中很多地方都利用到了先进先出的思想,比如作业调度(先来先服务),为什么这个原则在很多地方都会

【ZYNQ MPSoC开发】lwIP TCP发送用于数据缓存的软件FIFO设计

设计背景        任务是在ZYNQ的PS上使用裸机运行lwIP协议栈使用TCP把PL端通过AXI DMA传来的将近100K采样率的ADC数据发送出去,但由于数据带宽很大,有853.3mbps,所以在每一次AXI DMA简单传输结束后,lwIP未必有足够的发送buffer立即把数据发送走,如果是发送完再进行下一次简单传输的思路,则会很大地限制了整个系统的带宽,一个简单的思路是每次传输完成后判

APUE-FIFO理解

1.FIFO是一种文件类型。struct stat结构体中st_mode字段指明该文件是否是FIFO类型。可使用宏S_ISFIFO进行测试。2.FIFO操作函数:mkfifo, open, read, write, close, unlink等对文件进行操作的函数。   注:打开FIFO时同其他文件一样可以指定打开标识(如非阻塞,默认是阻塞打开),以及文件访问权限。3.阻塞打开FIFO:只读

LRU、LFU、FIFO算法总结

一、概述 (1)FIFO:First In First Out,先进先出 (2)LRU:Least Recently Used,最近最少使用 (3)LFU:Least Frequently Used,最不经常使用    FIFO表示先进先出,类似于对列,在数据的结构上使用对列来实现。 结构图: 1. 新访问的数据插入FIFO队列尾部,数据在FIFO

OutOfRangeError PaddingFIFOQueue '_1_get_batch/batch/padding_fifo_queue 解决方案

最近使用Faster-RCNN训练模型时,遇到了如标题所示的问题,最终得到解决,现在记录解决方式如下: 一般这种问题都不是代码的问题,请先检查训练数据: 1. 训练数据中图像文件和标注文件数量是否相同 2. 训练数据中是否有损坏的图片(数量多的话可以用PIL写个简单的加载方法去判断) 3. 标注文件中标注的长宽与实际长宽是否相同(我的问题在这里得到了解决,下面列出检测的代码): from

FPGA开发——使用verilog实现异步FIFO

一、FIFO 介绍 1、FIFO的分类 FIFO ( First In First Out)先进先出存储器。根据接入的时钟信号,可以分为同步 FIFO 和异步 FIFO 。 FIFO 底层是基于双口 RAM ,同步 FIFO 的读写时钟一致,异步 FIFO 读时钟和写时钟不同。 作用:  同步 FIFO:主要用于数据缓冲,类似于乒乓缓存思

[ip核][vivado]FIFO 学习

<xlinx FPGA应用进阶 通用IP核详解和设计开发>读书摘录: 1.        2.3.仿真模型 特点总结:1)复位后会有busy状态,需要等待wr_rst_busy信号低电平后才能正常写入                  2)prog_full信号的高电平长度可调                  3)仿真中的读状态很奇怪,并没有正常读取,都是XXX的状态。 所用的te

什么是FIFO管理单元?(First-In-First-Out)

FIFO(First-In-First-Out,先进先出)管理单元是一种广泛用于数据处理和存储系统中的机制,其核心理念是确保最早进入系统的数据最早被处理或移出。这种管理方法类似于排队的方式,最早进入队列的项目会最先得到服务。         FIFO管理单元通常用于缓冲区(Buffer)设计、任务调度、内存管理等多个领域。在硬件和软件系统中,FIFO机制有助于保证数据的有序处理,