【Linux102】41-kernel/blk_drv/hd.c


公粽号「专注Linux」,专注Linux内核开发

大地点

Linux102系列会详细讲解Linux0.11版本中的102个文件,本文讲解linux0.11的第41个文件【Linux102】41-kernel/blk_drv/hd.c的文件源码。

1. hd.c的主要作用

hd.c程序是硬盘控制器驱动程序,提供对硬盘控制器块设备的读写驱动硬盘初始化处理。程序中所有函数按照功能不同可分为5类:

  • 初始化硬盘设置硬盘所用数据结构信息的函数,如sys_setup()和hd_init()。

  • 硬盘控制器发送命令的函数hd_out()。

  • 处理硬盘当前请求项的函数do_hd_request()。

  • 硬盘中断处理过程中调用的C函数,如read_intr()、write_intr()、bad_rw_intr()和recal intr()。do_hd_request()函数也将在read intr()和write_intr()中被调用。

  • 硬盘控制器操作辅助函数,如controler_ready()、drive_busy()、win_result()、hd_out()和reset_controller()等。


sys_setup() 函数利用 boot/setup.s 程序提供的信息对系统中所含硬盘驱动器的参数进行
了设置

然后读取硬盘分区表,并尝试把启动引导盘上的虚拟盘根文件系统映像文件复制到内存虚拟盘中,若成功则加载虚拟盘中的根文件系统,否则就继续执行普通根文件系统加载操作


hd_init() 函数用于在内核初始化时设置硬盘控制器中断描述符,并复位硬盘控制器中断屏蔽码,以允许硬盘控制器发送中断请求信号。


hd_out()是硬盘控制器操作命令发送函数。该函数带有一个中断过程中调用C函数指
针参数
,在向控制器发送命令之前,它首先使用这个参数预置好中断过程中会调用的函数指针(do_hd),然后它按照规定的方式依次向硬盘控制器 0x1f0 至 0x1f7 发送命令参数块。除控制
器诊断(WIN_DIAGNOSE)和建立驱动器参数(WIN_SPECIFY)两个命令以外,硬盘控制器在接收到任何其他命令并执行了命令以后,都会向 CPU发出中断请求信号,从而引发系统去执行硬盘中断处理过程(在 system_call.s 中的 221 行)。


do_hd_request()硬盘请求项操作函数。其操作流程如下:

  • 首先判断当前请求项是否存在,若当前请求项指针为空,则说明目前硬盘块设备已经没有待处理的请求项,因此**立刻退出程序。**这是在宏 INIT_REQUEST 中执行的语句。否则就继续处理当前请求项。

  • 对当前请求项中指明的设备号和请求的盘起始扇区号合理性进行验证。

  • 根据当前请求项提供的信息计算请求数据磁盘磁道号磁头号柱面号。

  • 如果复位标志(reset)已被设置,则也设置硬盘重新校正标志(recalibrate),并对硬盘执行复位操作,向控制器重新发送**“建立驱动器参数”**命令(WIN_SPECIFY)。该命令不会引发硬盘中断。

  • 如果重新校正标志被置位的话,就向控制器发送硬盘重新校正命令(WIN_RESTORE),并在发送之前预先设置好该命令引发的中断中需要执行的 C 函数(recal_intr() ),并退出。

    recal_intr() 函数的主要作用是:当控制器执行该命令结束并引发中断时,能重新(继续)执行本函数。

  • 如果当前请求项指定是写操作,则首先设置硬盘控制器调用的 C 函数为 write_intr() ,向控制器发送写操作的命令参数块,并循环查询控制器的状态寄存器,以判断请求服务标志(DRQ)是否置位。若该标志置位,则表示控制器已“同意”接收数据,于是接着就把请求项所指缓冲区中的数据写入控制器的数据缓冲区中。若循环查询超时后该标志仍然没有置位,则说明此次操作失败。于是调用 bad_rw_intr() 函数,根据处理当前请求项发生的出错次数来确定是放弃继续处理当前请求项还是需要设置复位标志,以继续重新处理当前请求项。

  • 如果当前请求项是读操作,则设置硬盘控制器调用的 C 函数为 read_intr() ,并向控制器发送`读盘操作命令。


write_intr() 是在当前请求项是写操作时被设置成中断过程`调用的 C 函数。控制器完成写盘命令后会立刻向 CPU 发送中断请求信号,于是在控制器写操作完成后就会立刻调用该函数。

该函数首先调用 win_result() 函数,读取控制器的状态寄存器,以判断是否有错误发生

若在写盘操作时发生了错误,则调用 bad_rw_intr() ,根据处理当前请求项发生的出错次数来确定是放弃继续处理当前请求项还是需要设置复位标志,以继续重新处理当前请求项。

若没有发生错误,则根据当前请求项中指明的·需写扇区总数,判断是否已经把此请求项要求的所有数据写盘了。

若还有数据需要写盘,则再把一个扇区的数据复制到控制器缓冲区中。

若数据已经全部写盘,则处理当前请求项的结束事宜:唤醒等待本请求项完成的进程、唤醒等待空闲请求项的进程(若有的话)、设置当前请求项所指缓冲区数据已更新标志、释放当前请求项(从块设备链表中删除该项)。最后继续调用 do_hd_request() 函数,以继续处理硬盘设备的其他请求项。


read_intr() 则是在当前请求项是读操作时被设置成中断过程中调用的 C 函数控制器在把指定的扇区数据从硬盘驱动器读入自己的缓冲区后,就会立刻发送中断请求信号。而该函数的主要作用就是把控制器中的数据复制到当前请求项指定的缓冲区中。

write_intr() 开始的处理方式相同,该函数首先也调用 ·win_result() 函数,读取控制器的状态寄存器,以判断是否有错误发生。若在读盘时发生了错误,则执行与 write_intr() 同样的处理过程。若没有发生任何错误,则从控制器缓冲区把一个扇区的数据复制到请求项指定的缓冲区中。然后根据当前请求项中指明的欲读扇区总数,判断是否已经读取了所有的数据。若还有数据要读,则退出,以等待下一个中断的到来。若数据已经全部获得,则处理当前请求项的结束事宜:唤醒等待当前请求项完成的进程、唤醒等待空闲请求项的进程(若有的话)、设置当前请求项所指缓冲区数据已更新标志、释放当前请求项(从块设备链表中删除该项)。最后继续调用 do_hd_request() 函数,以继续处理硬盘设备的其他请求项。

为了能更清晰地看清楚硬盘读写操作的处理过程,我们可以把这些函数、中断处理过程以及硬盘控制器三者之间的执行时序关系用下图表示出来:



由以上分析可以看出,本程序中最重要的4个函数是 hd_out()do_hd_request()read_intr()write_intr()。理解了这4个函数的作用也就理解了硬盘驱动程序的操作过程。


2.源码用到的文件

必看硬件知识补充!

详见此文:hd.c的硬件补充知识


😉【Linux102】15-include/linux/sched.h 本程序主要定义了进程调度的相关数据结构,如任务数据结构等等,理解这些数据结构是理解进程调度机制的关键,也是理解内核的关键。一句话:**我们常说的进程是XXX,但是进程究竟是什么?**就定义在这个文件里面!源码面前了,没有秘密!
😉【Linux102】20-include/linux/kernel.h 本程序主要定义了内核中最常用的一些基础函数声明,是内核运行最基本保障,比如malloc、printk、free、tyy_write、panic等函数声明,这些函数具体的实现则分布在各个.c文件中。
😉【Linux102】21-include/asm/segment.h 本程序定义了一系列用于在 Linux 内核中访问用户空间内存内联函数,主要通过内嵌汇编操作段寄存器fs来实现内核与用户空间的数据交互。
😉【Linux102】24-include/linux/fs.h 这个文件(fs.h)是操作系统内核中文件系统模块的核心头文件,它的主要作用是定义文件系统实现所需的数据结构、常量、宏和函数接口,为整个文件系统的运作提供基础框架。
😉【Linux102】36-include/asm/system.h 该文件中定义了设置或修改描述符/中断门等嵌入式汇编宏。其中,函数 move_to_user_mode() 用于内核在初始化结束时 “切换”到初始进程(任务0)。所使用的方法是模拟中断调用返回过程,即利用指令iret运行初始任务0。
😉【Linux102】38-include/linux/fdreg.h 这个头文件为操作软盘控制器提供了统一的符号定义函数接口声明,是底层软盘驱动程序开发的基础,使得开发者可以通过这些抽象的符号和函数来与软盘控制器硬件进行交互,而无需直接使用具体的数值地址和命令码,提高了代码的可读性和可维护性。
😉【Linux102】39-include/asm/io.h io.h定义了一系列宏,这些宏是 x86 架构下直接操作硬件 I/O 端口的底层工具。
😉【Linux102】42-include/linux/config.h config.h 是一个配置文件,用于设置键盘布局和硬盘参数(备用)。设计目的:提供可配置的选项,适配不同的硬件环境。
😉【Linux102】43-include/linux/hdreg.h 这段代码是 hdreg.h 头文件,主要作用是定义与AT 硬盘控制器交互的核心常量、命令和数据结构,为硬盘驱动程序(如 hd.c)提供底层硬件接口的抽象。
😉【Linux102】40-kernel/blk_drv/blk.h 这是有关硬盘块设备参数的头文件,因为只用于块设备,所以与块设备代码放在同一个地方。其中主要定义了请求等待队列的数据结构request,用宏语句定义了电梯搜索算法,并对内核目前支持的虚拟盘硬盘软盘三种块设备,根据它们各自的主设备号分别设定了常数值。

3.源码版

/*
 *  linux/kernel/hd.c
 *
 *  (C) 1991  Linus Torvalds
 */

/*
 * This is the low-level hd interrupt support. It traverses the
 * request-list, using interrupts to jump between functions. As
 * all the functions are called within interrupts, we may not
 * sleep. Special care is recommended.
 *
 *  modified by Drew Eckhardt to check nr of hd's from the CMOS.
 */

#include <linux/config.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>

#define MAJOR_NR 3
#include "blk.h"

#define CMOS_READ(addr) ({
     
          \
    outb_p(0x80 | addr, 0x70); \
    inb_p(0x71);               \
})

/* Max read/write errors/sector */
#define MAX_ERRORS 7
#define MAX_HD 2

static void recal_intr(void);

static int recalibrate = 0;
static int reset = 0;

/*
 *  This struct defines the HD's and their types.
 */
struct hd_i_struct
{
   
   
    int head, sect, cyl, wpcom, lzone, ctl;
};
#ifdef HD_TYPE
struct hd_i_struct hd_info[] = {
   
   HD_TYPE};
#define NR_HD ((sizeof(hd_info)) / (sizeof(struct hd_i_struct)))
#else
struct hd_i_struct hd_info[] = {
   
   {
   
   0, 0, 0, 0, 0, 0}, {
   
   0, 0, 0, 0, 0, 0}};
static int NR_HD = 0;
#endif

static struct hd_struct
{
   
   
    long start_sect;
    long nr_sects;
} hd[5 * MAX_HD] = {
   
   
    {
   
   0, 0},
};

#define port_read(port, buf, nr) \
    __asm__("cld;rep;insw" ::"d"(port), "D"(buf), "c"(nr))

#define port_write(port, buf, nr) \
    __asm__("cld;rep;outsw" ::"d"(port), "S"(buf), "c"(nr))

extern void hd_interrupt(void);
extern void rd_load(void);

/* This may be used only once, enforced by 'static int callable' */
int sys_setup(void *BIOS)
{
   
   
    static int callable = 1;
    int i, drive;
    unsigned char cmos_disks;
    struct partition *p;
    struct buffer_head *bh;

    if (!callable)
        return -1;
    callable = 0;
#ifndef HD_TYPE
    for (drive = 0; drive < 2; drive++)
    {
   
   
        hd_info[drive].cyl = *(unsigned short *)BIOS;
        hd_info[drive].head = *(unsigned char *)(2 + BIOS);
        hd_info[drive].wpcom = *(unsigned short *)(5 + BIOS);
        hd_info[drive].ctl = *(unsigned char *)(8 + BIOS);
        hd_info[drive].lzone = *(unsigned short *)(12 + BIOS);
        hd_info[drive].sect = *(unsigned char *)(14 + BIOS);
        BIOS += 16;
    }
    if (hd_info[1].cyl)
        NR_HD = 2;
    else
        NR_HD = 1;
#endif
    for (i = 0; i < NR_HD; i++)
    {
   
   
        hd[i * 5].start_sect = 0;
        hd[i * 5].nr_sects = hd_info[i].head *
                             hd_info[i].sect * hd_info[i].cyl;
    }

    /*
        We querry CMOS about hard disks : it could be that
        we have a SCSI/ESDI/etc controller that is BIOS
        compatable with ST-506, and thus showing up in our
        BIOS table, but not register compatable, and therefore
        not present in CMOS.

        Furthurmore, we will assume that our ST-506 drives
        <if any> are the primary drives in the system, and
        the ones reflected as drive 1 or 2.

        The first drive is stored in the high nibble of CMOS
        byte 0x12, the second in the low nibble.  This will be
        either a 4 bit drive type or 0xf indicating use byte 0x19
        for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.

        Needless to say, a non-zero value means we have
        an AT controller hard disk for that drive.


    */

    if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
        if (cmos_disks & 0x0f)
            NR_HD = 2;
        else
            NR_HD = 1;
    else
        NR_HD = 0;
    for (i = NR_HD; i < 2; i++)
    {
   
   
     
我正在编辑【verilog】代码,遇到了 【DRC MDRV-11 Multiple Driver Nets: Net «consto> has multiple drivers: inst1 _wr_tinst1 _rt write_bram/U0/inst_blk_mem_gen/gnbram.gnativebmg.native_blk_mem_gen/valid. cstr/ramloop[1].ram.riprim noinit ram/DEVICE_ 7SERIES NO _BMM _INFO.TRUE_DP.SIMPLE_PRIM36.ram/DOADO(0]. inst1 _wr_It/inst1_t write bram/U0/inst blk_mem gen/gnbram.gnativebmg.native_ blk_mem _genvalid.cstr/ramloop{O].ram.r/prim_noinit ram/DEVICE 7SERIES.NO_EMM _INFO.TRUE_DP.SIMPLE_PRIM36.ram/DOADO(11 inst1_wr_inst1_It write_bram/U0/inst blk_mem gen/gnbram.gnativebmg.native_blk mem gen/valid. cstr/ramloop[1].ram.r/prim _noinit ram/DEVICE_/SERIES NO_EMM _INFO.TRUE_DP. SIMPLE_PRIM36.ram/DOADO/1. inst1 _wr_Itinst1_it write bram/U0/inst blk _mem gen/gnbram.gnativebmg.native_ bIk_mem_genvalid.cstr/ramloopfO].ram.r/prim_noinit.ram/DEVICE_/SERIES.NO_EMM_INFO.TRUE_DP.SIMPLE_PRIM36.ram/DOADO(2). inst1 _wr_It/inst1 rt write bram/U0/inst blk_mem _gen/gnbram.gnativebmg.native_ bik_mem genvalid.cstr/ramloop[1].ram.r/prim _noinit ram/DEVICE_7SERIESNO_EMM INFO.TRUE_DP.SIMPLE_PRIM36.ram/DOADOIZI. inst1_wr_rinst1_t write_bram/U0/inst _blk_mem gen/gnbram.gnativebmg.native_blk _mem gen~valid.cstr/ramloop[OJ.ram.r/prim_noinit.ram/DEVICE_7SERIES.NO BMM _INFO.TRUE_DP.SIMPLE_PRIM36.ram/DOAD0(3). inst1_wr_inst1_It write_ bram/U0/inst_blk_mem_gen/gnbram.gnativebmg.native_blk_mem_gen/valid.cstr/ramloop[1].ram.r/prim_noinitram/DEVICE_7SERIES.NO_BMM_INFO.TRUE_DP.SIMPLE_PRIM36.ram/DOADO(3). inst1 _wr_inst1 it write_bram/U0/inst_blk_mem gen/gnbram.gnativebmg.native _blk_mem genvalid. cstr/ramloop{O].ram.r/prim_.noinitram/DEVICE_ 7SERIES.NO_BMM INFO.TRUE_DP.SIMPLE_ PRIM36.ram/DOADO(47. inst1 _wr_inst1_rt write bram/U0/inst bik_mem gen/gnbram.gnativebmg.native_blk_mem genvalid cstr/ramloopf1). ram.r/prim noinit.ram/DEVICE 7SERIES.NO_ EMM INFO.TRUE DP.SIMPLE PRIM36.ramDOADOHI inst1 wr_Itinst1 it write bram/U0/inst_blk_mem_gen/gnbram.gnativebmg.native_blk_mem_genvalid.cstr/ramloop/Oj.ram.r/prim_noinitram/DEVICE_7SERIES.NO_EMM_INFO.TRUE_DP.SIMPLE_PRIM36.ram/DOAD0(5]. inst1 _wr Minst1 It write _bram/UO/inst_blk_mem_gen/gnbram.gnativebmg.native_bIk_memgenvalid.cstriramloop/1].ram.r/prim_noinitram/DEVICE_7SERIESNO_EMM_INFO.TRUE_DP.SIMPLE_PRIM36.ram/DOAD0(5]. An-tM NrHhnEtA Rrita hramnininat Hiklmam noninnhram anativ/ahma native hik mam nontalid retriramionnihtram rinrim InninitraminEVInE 7CERIECNA RUM INEA TRIIE} ,请帮我检查并改正错误点。我的原始代码如下
03-28
<think>好的,我现在需要帮助用户解决Verilog代码中的DRC MDRV-11错误,也就是多个驱动器驱动同一个网络的问题。首先,我需要理解用户提供的错误信息。从错误信息来看,多个实例驱动了同一个网络“consto”,具体涉及到不同的ramloop索引,比如ramloop[0]、ramloop[1]等,以及不同的DOADO端口位,如DOADO(0)、DOADO(1)等。 接下来,我需要分析用户提供的原始代码,但用户可能还没有给出代码。不过,根据常见的Verilog设计错误,这种情况通常发生在多个驱动程序同时写入同一个信号或存储器端口。特别是在使用双端口块存储器(True Dual Port RAM)时,如果两个端口同时写入同一个地址,可能会导致多驱动问题。 首先,我应该检查是否在代码的不同部分错误地实例化了多个块存储器(Block RAM)并连接到同一个输出信号。例如,可能在不同的always块或模块实例中,多个驱动器被连接到同一个输出端口,导致冲突。 另一个可能的原因是地址生成逻辑有问题。如果两个端口的地址信号在某种情况下重叠,导致同一时间两个端口都试图写入同一个存储位置,这也会触发多驱动错误。需要检查地址生成逻辑,确保两个端口的地址不会同时指向同一个位置。 此外,还要检查是否在组合逻辑中错误地赋值了同一个信号,比如在多个always块中对同一个寄存器变量进行赋值,或者在实例化模块时多次连接到同一个输出网络。 如果用户使用的是Xilinx的Block Memory Generator,需要确认配置是否正确,特别是是否设置为True Dual Port模式,并且每个端口有独立的控制信号。同时,检查是否有意外的端口连接,例如将两个端口的写使能同时激活,并且地址相同。 可能的解决步骤包括: 1. 检查所有实例化的存储器模块,确保每个输出信号只有一个驱动器。 2. 确认地址生成逻辑,避免两个端口同时写入同一地址。 3. 检查写使能信号,确保不会同时激活多个写操作到同一位置。 4. 如果使用多个块存储器实例,确保它们的输出通过适当的逻辑(如多路复用器)进行选择,而不是直接连接到同一网络。 最后,建议用户分享相关代码片段,特别是涉及块存储器实例化和连接的部分,以便更具体地定位问题并提供修正建议。</think>根据您提供的错误信息,这是一个典型的Verilog多驱动问题(Multiple Driver Nets)。以下是逐步分析和解决方案: ============================ 【问题定位】 1. 错误类型:DRC MDRV-11 2. 错误网络:`consto` 3. 根本原因:同一个信号被多个驱动源同时驱动 4. 关键线索:错误路径中反复出现`ramloop[0]/ramloop[1]`和`DOADO`端口,表明与双端口块存储器的连接冲突有关 ============================ 【常见错误场景】 1. 多实例冲突: - 不同存储器实例的`DOADO`输出直接连接到同一网络 - 例如同时使用`ramloop[0]`和`ramloop[1]`的输出到同一信号 2. 控制信号错误: ```verilog // 错误示例:两个always块驱动同一信号 always @(posedge clk) begin if (wea) dout <= ram[addr]; end always @(posedge clk) begin if (web) dout <= ram[addr]; // 冲突! end ``` 3. 存储器配置错误: - 使用Xilinx Block Memory Generator时: - ▢ True Dual Port模式未正确配置独立端口 - ▢ 两个端口的输出缓冲区未隔离 ============================ 【解决方案】 1. 隔离输出端口: ```verilog // 正确示例:使用独立输出寄存器 reg [31:0] dout_a, dout_b; always @(posedge clk) begin if (wea) dout_a <= ram[addra]; if (web) dout_b <= ram[addrb]; // 独立寄存器 end assign final_out = sel ? dout_a : dout_b; // 添加选择逻辑 ``` 2. 检查存储器实例化: ```verilog // 确保每个实例有独立输出 blk_mem_gen_0 ram0 ( .clka(clk), .addra(addra), .douta(dout0), // 独立输出信号 //... ); blk_mem_gen_1 ram1 ( .clka(clk), .addra(addra), .douta(dout1), // 独立输出信号 //... ); ``` 3. 关键配置检查: - 在Vivado Block Memory Generator中: - ✓ Port A Options → Enable Port A Output Registers - ✓ Port B Options → Enable Port B Output Registers - ✓ 勾选"Use Output Register"选项 ============================ 【验证步骤】 1. 运行`report_drc -ruledeck {MDRV-11}` 2. 使用网表查看器定位`consto`网络: - 右键选择"Mark Debug" - 在ILA中添加调试探针 3. 检查综合后的网表: - 确认每个DOADO端口只连接到一个负载 - 验证地址生成逻辑无重叠 建议提供具体涉及存储器读写的代码片段,我可以进一步帮助检查信号连接关系。该错误通常需要通过输出缓冲寄存器和地址隔离机制来解决。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值