基于FPGA的CMOS摄像机图像采集 一、数据采集系统概述 数据采集是指将以各种形式输入的被测信号,包括语音信号、温度信号、湿度信号、图像信号等经过适当处理,成为计算机可以识别的数字信号,从而送入计算机进行存储处理的过程,数据采集卡就是典型的基于数据采集系统原理的集成计算机扩展卡。如图1所示,在数据采集过程中主要有几个关键部分:(1)输入信号的幅度较小或者过大,需要经过放大器单元将输入信号幅度放大或者缩小;(2)输入信号带有较大的噪声,需要经过一个硬件的模拟滤波单元,将信号滤波整形;(3)将信号送到AD进行模数转换;(4)将信号传输到计算机;(5)存储记录和处理数据。 例如在一个具有两个Bank的SDRAM的模组中,其中一个Bank在进行预充电期间,另一个Bank却马上可以被读取,这样当进行一次读取后,又马上去读取已经预充电Bank的数据时,就无需等待而是可以直接读取了,这也就大大提高了存储器的访问速度。其优点是设计相对简单、不许考虑传输时转换总线的问题、较为可靠,缺点是SDRAM容量有限、传输时序需要计算准确、SDRAM经常刷新等。 根据综合分析和考虑,我们选用相对简单方便的方案2,选择器件如下:FPGA采用Altera的Cyclone EP2C35F484C8,视频采集芯片用飞利浦的SAA7113作为A/D转换单元,两块型号为K4S641632E的SDRAM和FPGA构成图像帧存储及传输处理系统,ADV7125是数模转换芯片,和计算机VGA显示器相连作为显示终端,系统整体框图如图2所示。 图2 视频监控系统整体框图 以上图像视频监视系统的大部分设计工作都集中在对FPGA的编程开发上,通过分析可以确定出FPGA需要包含如下几个功能模块。 (1)视频接口配置模块:视频采集芯片SAA7113具有多种采集方式,这里FPGA通过I²C总线对其内部寄存器进行配置,使其按照一定的格式进行采样。 (2)异步FIFO模块:当FPGA接收A/D采样的视频数据时,由于SAA7113和FPGA一般在不同的时钟频率下,这就会出现通常所说的异步时钟问题,处理不当就容易出现亚稳态,常用方法是在两者之间添加一块异步FIFO。 (3)视频变换模块:对得到的数字视频流进行解码,识别出行、场同步信号,并且根据需要选择采集图像的大小,进而变换成RGB格式的图像数据,以便于后续显示。 (4)图像帧存读写模块:将解码后的数据经由一个乒乓机制依次存放在两片RAM中,每个里面刚好存放一幅图像,通过乒乓机制使得两块存储区域交替进行存储输入和显示输出,避免等待,提高速度。 (5)VGA控制模块:根据VGA的工业参数,产生相应的行同步和场同步信号,并在适当时刻送入数据,经由ADV7125送VGA进行显示。 图3描述了FPGA内部的各个主要功能模块。系统上电时,FPGA首先从外部Flash中读取配置数据,完成自身的程序加载,进入工作模式状态。随后I²C配置接口模块完成对 2 / 20 SAA7113的初始化,初始化结束后,FPGA等待采集图像的命令。FPGA收到采集命令后,启动采集视频数据模块、异步FIFO模块和视频解码模块进行解码,将数据轮换写到两个帧存中,经通信模块送出,以上即为该系统的工作流程。下面主要针对SDRAM控制模块的具体实现过程进行详细分析和介绍。 3 / 20 (1)SDRAM的初始化 SDRAM在上电100~200µs后,必须由一个初始化进程来配置SDRAM的模式存储器,模式存储器的值决定SDRAM的工作模式。 访问存储单位:为减少I/O引脚数量,SDRAM复用地址线,所有在读写SDRAM时,先由ACTIVE命令激活要读写的BANK,并锁存行地址,然后在读写指令有效时锁存列地址。一旦BANK被激活后,只有执行一次预充命令后才能再次激活同一BANK。 (2)刷新和预充 SDRAM的存储单元可以理解为一个电容,总是倾向于放电,因此必须有定时刷新周期以避免数据全失。刷新周期可由(最小刷新周期+时候周期)计算获得。对BANK预充电或者关闭已激活的BANK,可预充特定BANK也可同时作用于所有BANK,A10、BA0和BA1用于选择BANK。 (3)操作控制 SDRAM的具体控制命令由一些专用控制引脚和地址线辅助完成。CS、RAS、CAS和WR在时钟上升沿的状态决定具体操作动作,地址线和BANK选择控制线在部分操作动作中作为辅助参数输入。由于特殊的存储结构,SDRAM操作指令比较多,不像SRAM一样只有简单的读写。根据系统要求,本设计选用SAMSUNG的K4S6432 SDRAM芯片。 3.2 SDRAM控制器总体设计 SDRAM控制器与外部的接口示意图由图4给出,控制器右端接口信号均为直接与SDRAM对应管脚相连的信号;控制器左端的接口信号为与FPGA相连的系统控制接口信号,其中,CLK为系统时钟信号,RESET_N为复位信号,ADDR为系统给出的SDRAM地址信号,DAIN是系统用于写入SDRAM的数据信号,FPGA_RD和FPGA_WR为系统读、写请求信号(1为有效,0为无效),SDRAM_FREE是SDRAM的空闲状态标示信号(0为空闲,1为忙碌),FDATA_ENABLE是控制器给系统的数据收发指示信号(为0时,无法对SDRAM进行数据收发;为1时,若是系统读操作,则系统此时可从DAOUT接收SDRAM的数据,若是写操作,则系统此时可以通过DAIN发送数据给SDRAM)。 4 / 20 5 / 20 图5 SDRAM控制器的结构图 3.3 SDRAM系统控制接口子模块设计 该模块主要包括初始化和系统指令分析功能。其工作过程如下:由计数器控制在系统上电约200µs后,先进行SDRAM的初始化配置工作,由一个Precharge all back指令完成对所有BANK的预充,接着是多个Refresh指令,然后是模式配置指令LOADMODE,完成SDRAM的工作模式设置。之后进行控制器的初始化配置工作,先发出指令LOADREG1给控制器载入模式字,再发出LOADREG2指令载入控制器的刷新计数器值,完成控制器初始化配置。 上述初始化过程结束后,系统指令分析机制才可接收并分析系统的读写信号和地址信息,以及从下个模块反馈回来的CMD_ACK信号,并产生对应的CMD命令和SADDR地址信息给CMD命令解析模块。通过程序设置,实现了根据初始化配置的参数来确定在读写到特定时刻发出Precharge或者Refresh的CMD指令,从而简化了系统的控制。而每当收到CMD_ACK为1时,表示CMD指令已经发出并有效,此时就要发出NOP命令(CMD=000)。要说明的是,SADDR是分时复用的,在初始化载入模式时,SADDR用以传输用户自己定义的模式字内容;而在正常的读写期间,SADDR作为地址线传输SDRAM所需的行、列和块地址。代码示例如附件1. 3.4 CMD命令解析和命令相应子模块 该模块首先对CMD指令进行判断,其结果解释输出相应的操作指令进行响应。例如,CMD为001时,则会输出do_read信号为1;CMD为010时,则会输出do_write信号为1,在同一时刻,只会输出一种有效的操作指令。然后该模块根据操作指令,做出符合SDRAM读写规范的操作动作,来进行用户期望的操作;给出数据选通信号OE,来控制数据通路模块(写操作OE为1,读操作时OE为0)。此外,该模块把系统非复用的地址ADDR处理为SDRAM复用的地址,分时送给SA、BA。程序中地址复用的方法为: 6 / 20 assign raddr=ADDR[ROWSTART+ROWSIZE-1:ROWSTART];//raddr为行地址 assign eaddr=ADDR[COLSTART+COLSIZE-1:COLSTART];//eaddr为列地址 assign baddr=ADDR[BANKSTART+BANKSIZE-1:BANKSTART];//baddr为BANK地址 在程序中,WRITEA和READA的CMD指令实际隐含了ACTIVE命令,所以该模块在收到do_write或do_read指令后,会先进行激活动作,经过初始化配置规定的CAS延迟时间之后再进行读写动作。 此外,该模块内含用以预设某些模式参数的模式寄存器,主要包括3类:第1类是SDRAM模式控制寄存器,在LOADMODE指令时,将该寄存器的值送入SDRAM的模式寄存器中,以控制SDRAM的工作模式;第2类是SDRAM控制器的参数寄存器(LOAD_REG1),使得SDRAM控制器的工作方式与外部的SDRAM器件的工作方式匹配;第3类是SDRAM的刷新周期控制寄存器,该寄存器预设用户定义的自动刷新计数值,用于SDRAM的刷新周期预设。上述3类寄存器的预设值都是系统控制接口模块在初始化时通过SADDR传送给来的。收到各类操作指令后,该模块会反馈给CMD命令解析模块cmdack信号为1,并最终反馈到系统控制接口模块的CMDACK信号为1,如果没有收到任何操作指令,则cmdack=0,CMDACK信号为0。代码示例如附件2. 3.5 数据通路子模块 该模块受OE信号的控制,使数据的进出和相应的操作指令在时序上同步。OE为1时,数据可由DQ脚写入SDRAM,OE为0时,数据可从SDRAM的DQ脚读出。因为是内部模块,所以应该尽量避免使用双向端口,因此在这里DQ的输入输出作用分别用端口DQIN和DQOUT代替,在顶层模块调用时再使用OE信号实现三态双向传输。代码示例如附件3. 3.6 SDRAM控制器顶层模块 实际上在大型工程开发过程中很少用到图形编辑工具,因为连接线较多不易连接,容易显得杂乱,可读性和可移植性都不强。因此大多数模块调用都是通过代码形式来调用的,读者要熟悉并习惯使用在程序中调用另一个子模块的方式。附件4是SDRAM控制器顶层模块代码示例,通过这种调用方式将其他子模块融合在一个统一的大工程下。 7 / 20 附件1: module control_interface (CLK, RESET_N, CMD, ADDR, REF_ACK, CM_ACK, NOP, READA, WRITEA, REFRESH,PRECHARGE, LOAD_MODE, SADDR, SC_CL, SC_RC, SC_RRD, SC_PM, SC_BL, REF_REQ, CMD_ACK); reg LOAD_REG1; reg LOAD_REG2; parameter ASIZE = 32; input CLK; input RESET_N; input[2:0] CMD; input[ASIZE - 1:0] ADDR; input REF_ACK; input CM_ACK; output NOP; reg NOP; output READA; reg READA; output WRITEA; reg WRITEA; output REFRESH; reg REFRESH; output PRECHARGE; reg PRECHARGE; output LOAD_MODE; reg LOAD_MODE; output[ASIZE - 1:0] SADDR; wire[ASIZE - 1:0] SADDR; output[1:0] SC_CL; wire[1:0] SC_CL; output[1:0] SC_RC; wire[1:0] SC_RC; output[3:0] SC_RRD; wire[3:0] SC_RRD; output SC_PM; wire SC_PM; output[3:0] SC_BL; wire[3:0] SC_BL; output REF_REQ; wire REF_REQ; output CMD_ACK; wire CMD_ACK; 8 / 20 9 / 20 wire[15:0] REF_PER; wire[15:0] timer; wire timer_zero; reg[ASIZE - 1:0] SAADR_int; wire CMD_ACK_int; wire[3:0] SC_BL_int; always @(posedge CLK or negedge RESET_N) begin if (RESET_N == 1'b0) begin NOP <= 1'b0 ; READA <= 1'b0 ; WRITEA <= 1'b0 ; REFRESH <= 1'b0 ; PRECHARGE <= 1'b0 ; LOAD_MODE <= 1'b0 ; LOAD_REG1 <= 1'b0 ; LOAD_REG2 <= 1'b0 ; SAADR_int <= {ASIZE - 1-(0)+1{1'b0}} ; end else begin SAADR_int <= ADDR ; if (CMD == 000) begin NOP <= 1'b1 ; end else begin NOP <= 1'b0 ; end if (CMD == 001) begin READA <= 1'b1 ; end else begin READA <= 1'b0 ; end if (CMD == 010) begin WRITEA <= 1'b1 ; end else begin end if (CMD == 011) WRITEA <= 1'b0 ; 10 / 20 begin REFRESH <= 1'b1 ; end else begin REFRESH <= 1'b0 ; end if (CMD == 100) begin PRECHARGE <= 1'b1 ; end else begin PRECHARGE <= 1'b0 ; end if (CMD == 101) begin LOAD_MODE <= 1'b1 ; end else begin LOAD_MODE <= 1'b0 ; end if ((CMD == 110) & (LOAD_REG1 == 1'b0)) begin LOAD_REG1 <= 1'b1 ; end else begin LOAD_REG1 <= 1'b0 ; end if ((CMD == 111) & (LOAD_REG2 == 1'b0)) begin LOAD_REG2 <= 1'b1 ; end else begin LOAD_REG2 <= 1'b0 ; end end end endmodule 附件2: module command (CLK, RESET_N, SADDR, NOP, READA, WRITEA, REFRESH, PRECHARGE, LOAD_MODE, SC_CL, SC_RC, SC_RRD, SC_PM, SC_BL, REF_REQ, REF_ACK, CM_ACK, OE, SA, BA, CS_N, CKE, RAS_N, CAS_N, WE_N); parameter ASIZE = 23; parameter DSIZE = 32; parameter ROWSIZE = 12; parameter COLSIZE = 9; parameter BANKSIZE = 2; parameter ROWSTART = 9; parameter COLSTART = 0; parameter BANKSTART = 20; input CLK; input RESET_N; input[ASIZE - 1:0] SADDR; input NOP; input READA; input WRITEA; input REFRESH; input PRECHARGE; input LOAD_MODE; input[1:0] SC_CL; input[1:0] SC_RC; input[3:0] SC_RRD; input SC_PM; input[3:0] SC_BL; input REF_REQ; output REF_ACK; wire REF_ACK; output CM_ACK; wire CM_ACK; output OE; reg OE; output[11:0] SA; wire[11:0] SA; output[1:0] BA; wire[1:0] BA; output[1:0] CS_N; wire[1:0] CS_N; 11 / 20 output CKE; wire CKE; output RAS_N; wire RAS_N; output CAS_N; wire CAS_N; output WE_N; wire WE_N; reg do_nop; reg do_reada; reg do_writea; reg do_writea1; reg do_refresh; reg do_precharge; reg do_load_mode; reg command_done; reg[7:0] command_delay; wire[3:0] rw_shift; wire do_act; reg rw_flag; wire do_rw; reg[7:0] oe_shift; reg oe1; reg oe2; reg oe3; reg oe4; reg[3:0] rp_shift; reg rp_done; wire[ROWSIZE - 1:0] rowaddr; wire[COLSIZE - 1:0] coladdr; wire[BANKSIZE - 1:0] bankaddr; wire REF_REQ_int; assign rowaddr = SADDR[ROWSTART + ROWSIZE - 1:ROWSTART] ; assign coladdr = SADDR[COLSTART + COLSIZE - 1:COLSTART] ; assign bankaddr = SADDR[BANKSTART + BANKSIZE - 1:BANKSTART] ; always @(posedge CLK or negedge RESET_N) begin if (RESET_N == 1'b0) begin do_nop <= 1'b0 ; do_reada <= 1'b0 ; 12 / 20 do_writea <= 1'b0 ; do_refresh <= 1'b0 ; do_precharge <= 1'b0 ; do_load_mode <= 1'b0 ; command_done <= 1'b0 ; command_delay <= {8{1'b0}} ; rw_flag <= 1'b0 ; rp_shift <= {4{1'b0}} ; rp_done <= 1'b0 ; do_writea1 <= 1'b0 ; end else begin if ((REF_REQ == 1'b1 | REFRESH == 1'b1) & command_done == 1'b0 & do_refresh == 1'b0 & rp_done == 1'b0 & do_reada == 1'b0 & do_writea == 1'b0) begin do_refresh <= 1'b1 ; end else begin do_refresh <= 1'b0 ; end if ((READA == 1'b1) & (command_done == 1'b0) & (do_writea == 1'b0) & (rp_done == 1'b0) & (REF_REQ == 1'b0)) begin do_reada <= 1'b1 ; end else begin do_reada <= 1'b0 ; end if ((WRITEA == 1'b1) & (command_done == 1'b0) & (do_writea == 1'b0) & (rp_done == 1'b0) & (REF_REQ == 1'b0)) begin do_writea <= 1'b1 ; do_writea1 <= 1'b1 ; end else begin do_writea <= 1'b0 ; do_writea1 <= 1'b0 ; end if ((PRECHARGE == 1'b1) & (command_done == 1'b0) & (do_writea == 1'b0) & (do_precharge == 1'b0)) 13 / 20 begin do_precharge <= 1'b1 ; end else begin do_precharge <= 1'b0 ; end if ((LOAD_MODE == 1'b1) & (command_done == 1'b0) & (do_load_mode == 1'b0)) begin do_load_mode <= 1'b1 ; end else begin do_load_mode <= 1'b0 ; end if ((do_refresh == 1'b1) | (do_reada == 1'b1) | (do_writea == 1'b1) | (do_precharge == 1'b1) | (do_load_mode == 1'b1)) begin command_delay <= 11111111 ; command_done <= 1'b1 ; rw_flag <= do_reada ; end else begin command_done <= command_delay[0] ; command_delay[6:0] <= command_delay[7:1] ; command_delay[7] <= 1'b0 ; end if ((command_delay[0]) == 1'b0 & command_done == 1'b1) begin rp_shift <= 1111 ; rp_done <= 1'b1 ; end else begin rp_done <= rp_shift[0] ; rp_shift[2:0] <= rp_shift[3:1] ; rp_shift[3] <= 1'b0 ; end end end always @(posedge CLK or negedge RESET_N) begin 14 / 20 if (RESET_N == 1'b0) begin oe_shift <= {8{1'b0}} ; oe1 <= 1'b0 ; oe2 <= 1'b0 ; oe3 <= 1'b0 ; oe4 <= 1'b0 ; OE <= 1'b0 ; end else begin if (SC_PM == 1'b0) begin if (do_writea1 == 1'b1) begin if (SC_BL == 0001) begin oe_shift <= {8{1'b0}} ; end else if (SC_BL == 0010) begin oe_shift <= 00000001 ; end else if (SC_BL == 0100) begin oe_shift <= 00000111 ; end else if (SC_BL == 1000) begin oe_shift <= 01111111 ; end oe1 <= 1'b1 ; end else begin oe_shift[6:0] <= oe_shift[7:1] ; oe_shift[7] <= 1'b0 ; oe1 <= oe_shift[0] ; oe2 <= oe1 ; oe3 <= oe2 ; oe4 <= oe3 ; if (SC_RC == 2'b10) begin OE <= oe3 ; 15 / 20 end else begin OE <= oe4 ; end end end else begin if (do_writea1 == 1'b1) begin oe4 <= 1'b1 ; end else if (do_precharge == 1'b1 | do_reada == 1'b1 | do_refresh == 1'b1) begin oe4 <= 1'b0 ; end OE <= oe4 ; end end end endmodule 附件3: module sdr_data_path (CLK, RESET_N, OE, DATAIN, DM, DATAOUT, DQIN, DQOUT, DQM); parameter DSIZE = 32; input CLK; input RESET_N; input OE; input[DSIZE - 1:0] DATAIN; input[DSIZE / 8 - 1:0] DM; output[DSIZE - 1:0] DATAOUT; wire[DSIZE - 1:0] DATAOUT; input[DSIZE - 1:0] DQIN; output[DSIZE - 1:0] DQOUT; wire[DSIZE - 1:0] DQOUT; output[DSIZE / 8 - 1:0] DQM; reg[DSIZE / 8 - 1:0] DQM; reg[DSIZE - 1:0] DIN1; reg[DSIZE - 1:0] DIN2; reg[DSIZE / 8 - 1:0] DM1; 16 / 20 always @(posedge CLK or negedge RESET_N) begin if (RESET_N == 1'b0) begin DIN1 <= {DSIZE - 1-(0)+1{1'b0}} ; DIN2 <= {DSIZE - 1-(0)+1{1'b0}} ; DM1 <= {DSIZE / 8 - 1-(0)+1{1'b0}} ; end else begin DIN1 <= DATAIN ; DIN2 <= DIN1 ; DM1 <= DM ; DQM <= DM1 ; end end assign DATAOUT = DQIN ; assign DQOUT = DIN2 ; endmodule 附件4: module sdr_sdram (CLK, RESET_N, ADDR, CMD, CMDACK, DATAIN, DATAINOUT, DM, SA, BA, CS_N, CKE, RAS_N, CAS_N, WE_N, DQ, DQM); parameter ASIZE = 23; parameter DSIZE = 32; parameter ROWSIZE = 12; parameter COLSIZE = 9; parameter BANKSIZE = 2; parameter ROWSTART = 9; parameter COLSTART = 0; parameter BANKSTART = 20; input CLK; input RESET_N; input[ASIZE - 1:0] ADDR; input[2:0] CMD; output CMDACK; wire CMDACK; input[DSIZE - 1:0] DATAIN; output[DSIZE - 1:0] DATAINOUT; wire[DSIZE - 1:0] DATAINOUT; input[DSIZE / 8 - 1:0] DM; 17 / 20 output[11:0] SA; reg[11:0] SA; output[1:0] BA; reg[1:0] BA; output[1:0] CS_N; reg[1:0] CS_N; output CKE; reg CKE; output RAS_N; reg RAS_N; output CAS_N; reg CAS_N; output WE_N; reg WE_N; inout[DSIZE - 1:0] DQ; wire[DSIZE - 1:0] DQ; wire[DSIZE - 1:0] DQ_xhdl0; output[DSIZE / 8 - 1:0] DQM; wire[DSIZE / 8 - 1:0] DQM; // <
> Unsupported Construct - attribute (source line 115) // <> Unsupported Construct - attribute (source line 124)) wire[11:0] ISA; wire[1:0] IBA; wire[1:0] ICS_N; wire ICKE; wire IRAS_N; wire ICAS_N; wire IWE_N; reg[DSIZE - 1:0] DQIN; wire[DSIZE - 1:0] IDATAOUT; wire[DSIZE - 1:0] DQOUT; wire[ASIZE - 1:0] saddr; wire[1:0] sc_cl; wire[1:0] sc_rc; wire[3:0] sc_rrd; wire sc_pm; wire[3:0] sc_bl; wire load_mode; wire nop; wire reada; wire writea; wire refresh; wire precharge; 18 / 20 wire oe; wire ref_req; wire ref_ack; wire cm_ack; wire CLK133; wire CLK133B; wire clklocked; assign DQ = DQ_xhdl0; control_interface #(ASIZE) control1( .CLK(CLK133), .RESET_N(RESET_N), .CMD(CMD), .ADDR(ADDR), .REF_ACK(ref_ack), .CM_ACK(cm_ack), .NOP(nop), .READA(reada), .WRITEA(writea), command #(ASIZE, DSIZE, ROWSIZE, COLSIZE, BANKSIZE, COLSTART, BANKSTART)//重新定义模块里的常量声明 command1( sdr_data_path #(DSIZE) data_path1( .CLK(CLK133), .RESET_N(RESET_N), .OE(oe), .DATAIN(DATAIN), .DM(DM), .DATAOUT(IDATAOUT), .DQM(DQM), .DQIN(DQIN), .DQOUT(DQOUT) ); ); .CLK(CLK133), .RESET_N(RESET_N), .SADDR(saddr), .NOP(nop), .READA(reada), .WRITEA(writea), .REFRESH(refresh), .PRECHARGE(precharge), .LOAD_MODE(load_mode), .SC_CL(sc_cl), .SC_RC(sc_rc), .SC_RRD(sc_rrd), .SC_PM(sc_pm), .SC_BL(sc_bl), .REF_REQ(ref_req), .REF_ACK(ref_ack), .CM_ACK(cm_ack), .OE(oe), .SA(ISA), .BA(IBA), .CS_N(ICS_N), .CKE(ICKE), .RAS_N(IRAS_N), .CAS_N(ICAS_N), .WE_N(IWE_N) .REF_REQ(ref_req), .CMD_ACK(CMDACK) ); .REFRESH(refresh), .precharge(precharge), .LOAD_MODE(load_mode), .SADDR(saddr), .SC_CL(sc_cl), .SC_RC(sc_rc), .SC_RRD(sc_rrd), .SC_PM(sc_pm), .SC_BL(sc_bl), // <> Can't find translated component 'pll1'. Port and module names may not match //pll u1(.clk(CLK), .locked(clklocked), .clock(CLK133),.reset(RESET_N)); 19 / 20 always @(posedge CLK133) begin SA <= ISA ; BA <= IBA ; CS_N <= ICS_N ; CKE <= ICKE ; RAS_N <= IRAS_N ; CAS_N <= ICAS_N ; WE_N <= IWE_N ; DQIN <= DQ ; DATAINOUT <= IDATAOUT ; // end assign DQ = (oe == 1'b1) ? DQOUT : {0{1'bz}} ; endmodule 20 / 20