【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十七:IIC储存模块 - FIFO读写
. int main() . { . int A; . A = ; . }
代码17.1
话题为进入之前,首先让我们来聊聊一些题外话。那些学过软核NIOS的朋友可曾记得,软核NIOS可利用片上内存作为储存资源,而且它也能利用SDRAM作为储存资源,然而问题是在这里 ... 如代码17.1所示,笔者先建立变量A,然后变量A赋值16。如果站在高级语言上的角度去思考,无论是建立变量A还是为变量A赋值,我们没有必要去理解变量A利用什么储存资源,然后赋值变量A又是利用怎样的储存功能去实现。
我们只要负责轻松的表层工作,然而那些辛苦的底层工作统统交由编译器处理。读者也许会认为那是高级语言的温柔,不过这股温柔却不适合描述语言,还不如说那是一种抹杀性的伤害。这种感觉好比父母亲过度溺爱自己的孩子,娇生惯养的孩子最终只会失去可能性而已。
笔者曾在前面说过,储存模块基本上可以分为“储存资源”还有“储存方式”。默认下,描述语言可用的储存资源有寄存器还有片上内存,然而两者都是内在资源。换之,实验十六却利用外在的储存资源,IIC储存器。
图17.1 IIC储存模块与RAM储存模块。
如图17.1所示,那是实验十六的IIC储存模块,然而图17.1也表示IIC储存模块的调用方法也不仅近似 RAM储存模块,而且只有一方调用它而已。这种感觉好比顺序语言的主函数调用某个储存函数一样,结果如代码17.2所示:
. int ram_func( int Addr, int WrData ) { ... } . . int main() . { . ram_func( , ); . ... . }
代码17.2
如代码17.2所示,第1行声明函数 ram_func,然后主函数在第5行将其调用,并且传递地址参数0,数据参数 20。高级语言是一位顺序又寂寞的家伙,函数永远只能被一方调用而已 ... 换之,描述语言是一位并行又多愁的家伙,模块有可能同时被两方以上调用,情况宛如两位男生同时追求一位少女,对此少女会烦恼选择谁。哎~这是奢侈的少女忧愁。
图17.2 双口RAM储存模块。
如图17.2所示,RAM储存模块同时被两方调用,周边操作为它写入数据,核心操作则为它读出数据。图17.2也是俗称的双口RAM。对此,我们也可以说双口RAM储存模是RAM储存模块的亚种。
图17.3 基于双口RAM储存模块的FIFO储存模块。
此外,双口RAM储存模块只要稍微更换一下马甲,然后加入一些先进先出的机制,随之基于双口RAM储存模块的FIFO储存模块便完成,结果如图17.3所示。对此,我们可以说FIFO储存模块是双口RAM储存模块的亚种。
那么问题来了:
“请问,实验十六的IIC储存模块是否也能成为双口IIC储存模块?”,笔者问道。
“再请问,它还可以成为有FIFO机制的储存模块呢?”,笔者再问道。
没错,上述问题就是实验十七的主要目的。如果这些要求可以成真,我们便可以断定描述语言不仅不逊色与顺序语言,描述语言也充满许多可能性,而且储存类作为一个模块类有着举足轻重的地位。废话少说,我们还是开始实验吧,因为笔者已经压抑不了蛋蛋的冲动!
首先我们要明白,片上内存是效率又优秀的储存资源,基本上只要1个时钟就可完成读写操作,而且读写也可以同时进行,两方也可以同时调用,不过就是不能随意扩充。反之,IIC储存器虽然可以随意扩充,但是又笨又麻烦的它,读写操作不仅用时很长,而且不能也同时进行,对此造就两方不能同时调用的问题。为此,我们必须先解决这个问题。
图17.4 写操作与读操作。
实验十六告诉我们,IIC储存模块有两位 Call 信号,其中 Call[1] 表示写操作,Call[0]表示读操作。不过不管是写操作还是读操作,IIC储存模块都必须调用IIC储存器,而且读写操作一次也只能进行其中一项。如图17.4,假设左边的周边操作负责写操作,右边的核心操作负责读操作 ... 如果两者同时拉高 Call 信号就会发生多义性的问题,对此笔者该如何协调呢?
图17.5 轮流协调。
为了公平起见,笔者采取轮流的方式来协调多义性的问题。图17.5所是轮流协调的概念图,一般Call兼职“提问与使能“,即Call一拉高操作便执行。如今Call信号作为第一层提问,isDo则作为第二层使能 ... 换句话说,不管 Call 拉不拉高,只要isDo不拉高,操作也不会执行。如图17.5所示,isDo位宽有3表示模块有3种操作,或者说3个操作共享一个模块资源。至于右边是称为使能指针的箭头,它的作用是给予使能权。
图17.6 轮流协调例子①。
如图17.6所示,假设 Call[2] 拉高以示提问,但是指针并没有指向它,所以它没有使能权也不能执行操作。这种情况好比举手的学生没被老师点名,这位学生就不能随意开口。当然,使能指针也不是静止不动,只要遇见有人举手提问,它便会按照顺序检测各个对象。
图17.7 轮流协调例子②。
如图17.7所示,当指针来到Call[2]的面前并且给予使能权,isDo[2]立即拉高使能操作,直至操作完成之前,该操作都享有模块的使用权。(灰度指针为过去,黑色指针为现在)
图17.8 轮流协调例子③。
如图17.8所示,操作执行完毕之际,模块便会反馈完成信号以示结束操作,isDo[2] 还有Call[2] 都会经由完成信号拉低内容。此外,指针也会立即指向下一个对象。
图17.9 轮流协调例子④。
如图17.9所示,假设 Call[2] 还有 Call[1] 同时提问,由于指针没有指向它们,所以Call[2] 与 Call[1] 都没有使能权。此刻,指针开始一动。
图17.10 轮流协调例子⑤。
首先,Call[1] 会得到使能权,isDo[1]因此拉高并且开始执行操作,直至操作结束之前,isDo[1]都独占模块,结果如图17.10所示。
图17.11 轮流协调例子⑥。
如图17.11所示,当操作执行完毕,模块便会反馈完成信号,随之isDo[1] 还有 Call[1]都会拉低内容,而且指针也会指向下一个对象。对此,isDo[2] 得到使能权,并且开始执行操作 ... 直至操作结束之前,它都独占模块。
图17.12 轮流协调例子⑦。
操作结束之际,模块便会反馈完成信号,isDo[2] 还有 Call[2] 随之也会拉低内容,然后指针指向另一个对象,结果如图17.12所示。轮流协调的概念基本上就是这样而已,即单纯也非常逻辑。接下来,让我们来看看Verilog 如何描述轮流协调,结果如代码17.3所示:
. module iic_savemod . ( . input [:]iCall, . output [:]oDone, . ); . reg [:]C7; . reg [:]isDo; . . always @ ( posedge CLOCK or negedge RESET ) . if( !RESET ) . begin . C7 <= ’b10; . isDo <= ’b00; . end . else . begin . if( iCall[] & C7[] ) isDo[] <= ’b1; . else if( iCall[] & C7[] ) isDo[] <= ’b1; . . if( isDo[] & isDone[] ) isDo[] <= ’b0; . else if( isDo[] & isDone[] ) isDo[] <= ’b0; . . if( isDone ) C7 <= { isDo[], isDo[] }; . else if( iCall ) C7 <= { C7[], C7[] }; . end .
代码17.3
第3~4行是相关的出入端声明, 其中 Call 还有 Done 均为两位。第6~7行是轮流协调作用的寄存器isDo与C7,C7为使能指针。第10~14行则是这些寄存器的初始化,注意C7默认下指向 Call[1]。第16~25行则是轮流协调的主要操作,第17~18行是提问与使能,其中 iCall[N] & C7[N] 表示提问并且被指针指向,isDo[N] 表示给予使能权。
第20~21行是消除提问和使能,其中 isDo[N] & isDone[N] 表示相关的完成信号对应相关的操作,然后 isDo[N] 表示消除使能。第24行的表示有提问,指针就立即移动。第23行表示结束操作,指针便指向下一个对象。
. reg [:]i; . reg [:]isDone; . . always @ ( posedge CLOCK or negedge RESET ) . if( !RESET ) . begin . ... . i <= ’d0; . isDone <= ’b00; . end . else if( isDo[] ) . case( i ) . ... . : begin isDone[] <= ’b1; i <= i + ’b1; end . : begin isDone[] <= ’b0; i <= ’d0; end . endcase . else if( isDo[] ) . case( i ) . ... . : begin isDone[] <= ’b1; i <= i + ’b1; end . : begin isDone[] <= ’b0; i <= ’d0; end . endcase . . endmodule
代码17.3
第27~28行只核心操作相关的寄存器,第31~36行则是这些寄存器的复位操作。第37行表示 isDo[1] 拉高才执行操作1。第40~41行表示操作1反馈完成信号。第43行表示 isDo[0] 拉高才指向操作0。第46~47行表示操作0反馈完成信号。如此一来,问答信号便有轮流协调,接下来就是为IIC储存模块加入FIFO机制。
图17.13 有FIFO机制的IIC储存模块。
如图17.13所示,那是拥有FIFO机制的IIC储存模块,它那畸形的储存功能,可谓是IIC储存模块的亚种。其中 Call/Done[1] 表示写入调用,Tag[1] 表示写满状态,反之既然。由于目前的IIC储存模块是FIFO的关系,所以写入地址还有读出地址都是在里边建立。为此,Verilog可以这样描述,结果如代码17.4所示:
. module iic_savemod . ( . input [:]iCall, . output [:]oDone, . input [:]iData, . output [:]oData, . output [:]oTag . ); . always @ ( posedge CLOCK or negedge RESET ) // 轮流协调的周边操作 . ... . . reg [:]C2,C3; // C2 Write Pointer, C3 Read Pointer; . . always @ ( posedge CLOCK or negedge RESET ) . if( !RESET ) . begin . C2 <= ’d0; . C3 <= ’d0; . end . else if( isDo[] ) . case( i ) . ... . : // Wirte Word Addr . begin D1 <= C2[:]; i <= FF_Write1; Go <= i + 'b1; end . ... . : . begin C2 <= C2 + 'b1; isDone[1] <= 1'b1; i <= i + 'b1; end . ... . endcase . else if( isDo[] ) . case( i ) . ... . : // Wirte Word Addr . begin D1 <= C3[:]; i <= FF_Write2; Go <= i + 'b1; end . ... . : . begin C3 <= C3 + 'b1; isDone[0] <= 1'b1; i <= i + 'b1; end . ... . endcase . . ... . assign oTag[] = ( (C2[]^C3[]) && (C2[:] == C3[:]) ); . assign oTag[] = ( C2 == C3 ); . . endmodule
代码17.4
如代码17.4所示,第3~7行是相关的出入端声明。第12行建立相关的寄存器,C2为写指针,C3为读指针,位宽为 N + 1。第23~24行表示C2[7:0]为写数据地址。第26~27行表示C2递增。第33~34行表示C3[7:0]为读数据地址。第36~37行表示C3递增。第42行表示写满状态,第43行则表示读空状态。完后,我们便可以开始建模了。
图17.14 实验十七的建模图。
图17.14是实验十七的建模图,周边操作为 IIC 储存模块写入数据,核心操作则从哪里读取数据,并且将读出的数据驱动数码管基础模块。
iic_savemod.v
图17.15 IIC储存模块。
图17.15是IIC储存模块的建模图,左方是写入操作,右边是读出操作,上方则是链接至顶层信号 SCL 与 SDA。
. module iic_savemod . ( . input CLOCK, RESET, . output SCL, . inout SDA, . input [:]iCall, . output [:]oDone, . input [:]iData, . output [:]oData, . output [:]oTag . ); . parameter FCLK = 'd125, FHALF = 10'd62, FQUARTER = 'd31; //(1/400E+3)/(1/50E+6) . parameter THIGH = 'd30, TLOW = 10'd65, TR = 'd15, TF = 10'd15; . parameter THD_STA = 'd30, TSU_STA = 10'd30, TSU_STO = 'd30; . parameter FF_Write1 = 'd7; . parameter FF_Write2 = 'd9, FF_Read = 5'd19; . . /***************/ . . reg [:]C7; . reg [:]isDo; . . always @ ( posedge CLOCK or negedge RESET ) . if( !RESET ) . begin . C7 <= 'b10; . isDo <= 'b00; . end . else . begin . . if( iCall[] & C7[] ) isDo[] <= 'b1; . else if( iCall[] & C7[] ) isDo[] <= 'b1; . . if( isDo[] & isDone[] ) isDo[] <= 'b0; . else if( isDo[] & isDone[] ) isDo[] <= 'b0; . . if( isDone ) C7 <= {isDo[],isDo[]}; . else if( iCall ) C7 <= { C7[], C7[] }; . . end . . . /***************/ . . reg [:]i; . reg [:]Go; . reg [:]C1; . reg [:]D1; . reg [:]isDone; . reg [:]C2,C3; // C2 Write Pointer, C3 Read Pointer . reg rSCL,rSDA; . reg isAck,isQ; . . always @ ( posedge CLOCK or negedge RESET ) . if( !RESET ) . begin . { i,Go } <= { 'd0,5'd0 }; . C1 <= 'd0; . D1 <= 'd0; . isDone <= 'd0; . { C2, C3 } <= 'd0; . { rSCL,rSDA,isAck,isQ } <= 'b1111; . end . else if( isDo[] ) . case( i ) . . : // Call . begin . isQ = ; . rSCL <= 'b1; . . if( C1 == ) rSDA <= 'b1; . else if( C1 == (TR + THIGH) ) rSDA <= 'b0; . . if( C1 == (FCLK) -) begin C1 <= 'd0; i <= i + 1'b1; end . else C1 <= C1 + 'b1; . end . . : // Write Device Addr . begin D1 <= {'b1010, 3'b000, 'b0}; i <= 5'd7; Go <= i + 'b1; end . . : // Wirte Word Addr . begin D1 <= C2[:]; i <= FF_Write1; Go <= i + 'b1; end . . : // Write Data . begin D1 <= iData; i <= FF_Write1; Go <= i + 'b1; end . . /*************************/ . . : // Stop . begin . isQ = 'b1; . . if( C1 == ) rSCL <= 'b0; . else if( C1 == FQUARTER ) rSCL <= 'b1; . . if( C1 == ) rSDA <= 'b0; . else if( C1 == (FQUARTER + TR + TSU_STO ) ) rSDA <= 'b1; . . if( C1 == (FQUARTER + FCLK) - ) begin C1 <= 'd0; i <= i + 1'b1; end . else C1 <= C1 + 'b1; . end . . : . begin C2 <= C2 + 'b1; isDone[1] <= 1'b1; i <= i + 'b1; end . . : . begin isDone[] <= 'b0; i <= 5'd0; end . . /*******************************/ //function . . ,,,,,,,: . begin . isQ = 'b1; . rSDA <= D1[-i]; . . if( C1 == ) rSCL <= 'b0; . else if( C1 == (TF + TLOW) ) rSCL <= 'b1; . . if( C1 == FCLK - ) begin C1 <= 'd0; i <= i + 1'b1; end . else C1 <= C1 + 'b1; . end . . : // waiting for acknowledge . begin . isQ = 'b0; . if( C1 == FHALF ) isAck <= SDA; . . if( C1 == ) rSCL <= 'b0; . else if( C1 == FHALF ) rSCL <= 'b1; . . if( C1 == FCLK - ) begin C1 <= 'd0; i <= i + 1'b1; end . else C1 <= C1 + 'b1; . end . . : . if( isAck != ) i <= 'd0; . else i <= Go; . . /*******************************/ // end function . . endcase . . else if( isDo[] ) . case( i ) . . : // Call . begin . isQ = ; . rSCL <= 'b1; . . if( C1 == ) rSDA <= 'b1; . else if( C1 == (TR + THIGH) ) rSDA <= 'b0; . . if( C1 == FCLK - ) begin C1 <= 'd0; i <= i + 1'b1; end . else C1 <= C1 + 'b1; . end . . : // Write Device Addr . begin D1 <= {'b1010, 3'b000, 'b0}; i <= 5'd9; Go <= i + 'b1; end . . : // Wirte Word Addr . begin D1 <= C3[:]; i <= FF_Write2; Go <= i + 'b1; end . . : // Start again . begin . isQ = 'b1; . . if( C1 == ) rSCL <= 'b0; . else if( C1 == FQUARTER ) rSCL <= 'b1; . else if( C1 == (FQUARTER + TR + TSU_STA + THD_STA + TF) ) rSCL <= 'b0; . . if( C1 == ) rSDA <= 'b0; . else if( C1 == FQUARTER ) rSDA <= 'b1; . else if( C1 == ( FQUARTER + TR + THIGH) ) rSDA <= 'b0; . . if( C1 == (FQUARTER + FCLK + FQUARTER) - ) begin C1 <= 'd0; i <= i + 1'b1; end . else C1 <= C1 + 'b1; . end . . : // Write Device Addr ( Read ) . begin D1 <= {'b1010, 3'b000, 'b1}; i <= 5'd9; Go <= i + 'b1; end . . : // Read Data . begin D1 <= 'd0; i <= FF_Read; Go <= i + 1'b1; end . . : // Stop . begin . isQ = 'b1; . . if( C1 == ) rSCL <= 'b0; . else if( C1 == FQUARTER ) rSCL <= 'b1; . . if( C1 == ) rSDA <= 'b0; . else if( C1 == (FQUARTER + TR + TSU_STO) ) rSDA <= 'b1; . . if( C1 == (FCLK + FQUARTER) - ) begin C1 <= 'd0; i <= i + 1'b1; end . else C1 <= C1 + 'b1; . end . . : . begin C3 <= C3 + 'b1; isDone[0] <= 1'b1; i <= i + 'b1; end . . : . begin isDone[] <= 'b0; i <= 5'd0; end . . /*******************************/ //function . . ,,,,,,,: . begin . isQ = 'b1; . . rSDA <= D1[-i]; . . if( C1 == ) rSCL <= 'b0; . else if( C1 == (TF + TLOW) ) rSCL <= 'b1; . . if( C1 == FCLK - ) begin C1 <= 'd0; i <= i + 1'b1; end . else C1 <= C1 + 'b1; . end . . : // waiting for acknowledge . begin . isQ = 'b0; . . if( C1 == FHALF ) isAck <= SDA; . . if( C1 == ) rSCL <= 'b0; . else if( C1 == FHALF ) rSCL <= 'b1; . . if( C1 == FCLK - ) begin C1 <= 'd0; i <= i + 1'b1; end . else C1 <= C1 + 'b1; . end . . : . if( isAck != ) i <= 'd0; . else i <= Go; . . /*****************************/ . . ,,,,,,,: // Read . begin . isQ = 'b0; . if( C1 == FHALF ) D1[-i] <= SDA; . . if( C1 == ) rSCL <= 'b0; . else if( C1 == FHALF ) rSCL <= 'b1; . . if( C1 == FCLK - ) begin C1 <= 'd0; i <= i + 1'b1; end . else C1 <= C1 + 'b1; . end . . : // no acknowledge . begin . isQ = 'b1; . //if( C1 == 100 ) isAck <= SDA; . . if( C1 == ) rSCL <= 'b0; . else if( C1 == FHALF ) rSCL <= 'b1; . . if( C1 == FCLK - ) begin C1 <= 'd0; i <= Go; end . else C1 <= C1 + 'b1; . end . . /*************************************/ // end fucntion . . endcase . . /***************************************/ . . assign SCL = rSCL; . assign SDA = isQ ? rSDA : 'bz; . assign oDone = isDone; . assign oData = D1; . assign oTag[] = ( (C2[]^C3[]) && (C2[:] == C3[:]) ); . assign oTag[] = ( C2 == C3 ); . . /***************************************/ . . endmodule
具体内容笔者也懒得解释了,读者自己看着办吧。
iic_demo.v
连线部署请参考图17.14。
. module iic_demo . ( . input CLOCK, RESET, . output SCL, . inout SDA, . output [:]DIG, . output [:]SEL . ); 以上内容为相关的出入端声明。 . reg [:]j; . reg [:]D1; . reg isWR; . . always @ ( posedge CLOCK or negedge RESET ) . if( !RESET ) . begin . j <= 'd0; . D1 <= 'd0; . isWR <= 'b0; . end . else . case( j ) . . : . if( !TagU1[] ) j <= j + 'b1; . . : . if( DoneU1[] ) begin isWR <= 'b0; j <= j + 1'b1; end . else begin isWR <= 'b1; D1 <= 8'hAB; end . . : . if( !TagU1[] ) j <= j + 'b1; . . : . if( DoneU1[] ) begin isWR <= 'b0; j <= j + 1'b1; end . else begin isWR <= 'b1; D1 <= 8'hCD; end . . : . if( !TagU1[] ) j <= j + 'b1; . . : . if( DoneU1[] ) begin isWR <= 'b0; j <= j + 1'b1; end . else begin isWR <= 'b1; D1 <= 8'hEF; end . . : . i <= i; . . endcase .
以上内容为写入作用的周边操作,操作过程如下:
步骤0,判断是否写满状态。
步骤1,写入数据 8’hAB;
步骤2,判断是否写满状态。
步骤3,写入数据 8’hCD;
步骤4,判断是否写满状态。
步骤5,写入数据 8’hEF;
步骤6,发呆。
. wire [:]DataU1; . wire [:]DoneU1; . wire [:]TagU1; . . iic_savemod U1 . ( . .CLOCK( CLOCK ), . .RESET( RESET ), . .SCL( SCL ), // > top . .SDA( SDA ), // <> top . .iCall( { isWR, isRD } ), // < sub & core . .oDone( DoneU1 ), // > core . .iData( D1 ), // < core . .oData( DataU1 ), // > core . .oTag( TagU1 ) . ); .
以上内容为IIC储存模块的实例化。第59行表示 isWR 为 Call[1],isRD 为 Call[0]。第61行表示 D1 驱动该输入。
. reg [:]i; . reg [:]D2; . reg isRD; . . always @ ( posedge CLOCK or negedge RESET ) // core . if( !RESET ) . begin . i <= 'd0; . D2 <= 'd0; . isRD <= 'b0; . end . else . case( i ) . . : . if( !TagU1[] ) i <= i + 'b1; . . : . if( DoneU1[] ) begin D2[:] <= DataU1; isRD <= 'b0; i <= i + 1'b1; end . else isRD <= 'b1; . . : . if( !TagU1[] ) i <= i + 'b1; . . : . if( DoneU1[] ) begin D2[:] <= DataU1; isRD <= 'b0; i <= i + 1'b1; end . else isRD <= 'b1; . . : . if( !TagU1[] ) i <= i + 'b1; . . : . if( DoneU1[] ) begin D2[:] <= DataU1; isRD <= 'b0; i <= i + 1'b1; end . else isRD <= 'b1; . . : . i <= i; . . endcase .
以上内容为读出数据并且驱动数码管基础模块的核心操作,操作过程如下:
步骤0,判断是否为读空状态。
步骤1,读出数据 8’hAB,并且暂存至 D2[23:16]。
步骤2,判断是否为读空状态。
步骤3,读出数据 8’hCD,并且暂存至 D2[15:8]。
步骤4,判断是否为读空状态。
步骤5,读出数据 8’hEF,并且暂存至 D2[7:0]。
步骤6,发呆。
. smg_basemod U2 . ( . .CLOCK( CLOCK ), . .RESET( RESET ), . .DIG( DIG ), // > top . .SEL( SEL ), // > top . .iData( D2 ) // < core . ); . . endmodule
第106~113行是数码管基础模块的实例化,第112行表示D2驱动该输入。编译完毕并且下载程序,如果数码管自左向右显示“ABCDEF”表示实验成功。
细节一: 完整的个体模块
实验十七的IIC储存模块随时可以使用。
【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十七:IIC储存模块 - FIFO读写的更多相关文章
- [黑金原创教程] FPGA那些事儿《设计篇 III》- 图像处理前夕·再续
简介 一本为入门图像处理的入门书,另外还教你徒手搭建平台(片上系统),内容请看目录. 注意 为了达到最好的实验的结果,请准备以下硬件. AX301开发板, OV7670摄像模块, VGA接口显示器, ...
- [黑金原创教程] FPGA那些事儿《设计篇 II》- 图像处理前夕·续
简介 一本为入门图像处理的入门书,另外还教你徒手搭建平台(片上系统),内容请看目录. 注意 为了达到最好的实验的结果,请准备以下硬件. AX301开发板, OV7670摄像模块, VGA接口显示器, ...
- [黑金原创教程] FPGA那些事儿《设计篇 I》- 图像处理前夕
简介 一本为入门图像处理的入门书,另外还教你徒手搭建平台(片上系统),内容请看目录. 注意 为了达到最好的实验的结果,请准备以下硬件. AX301开发板, OV7670摄像模块, VGA接口显示器, ...
- [黑金原创教程] FPGA那些事儿《数学篇》- CORDIC 算法
简介 一本为完善<设计篇>的书,教你CORDIC算法以及定点数等,内容请看目录. 贴士 这本教程难度略高,请先用<时序篇>垫底. 目录 Experiment 01:认识CORD ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】原创教程连载导读【连载完成,共二十九章】
前言: 无数昼夜的来回轮替以后,这本<驱动篇I>终于编辑完毕了,笔者真的感动到连鼻涕也流下来.所谓驱动就是认识硬件,还有前期建模.虽然<驱动篇I>的硬件都是我们熟悉的老友记,例 ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】连载导读
前言: 无数昼夜的来回轮替以后,这本<驱动篇I>终于编辑完毕了,笔者真的感动到连鼻涕也流下来.所谓驱动就是认识硬件,还有前期建模.虽然<驱动篇I>的硬件都是我们熟悉的老友记,例 ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十八:SDRAM模块① — 单字读写
实验十八:SDRAM模块① — 单字读写 笔者与SDRAM有段不短的孽缘,它作为冤魂日夜不断纠缠笔者.笔者尝试过许多方法将其退散,不过屡试屡败的笔者,最终心情像橘子一样橙.<整合篇>之际, ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十五:FIFO储存模块(同步)
实验十五:FIFO储存模块(同步) 笔者虽然在实验十四曾解释储存模块,而且也演示奇怪的家伙,但是实验十四只是一场游戏而已.至于实验十五,笔者会稍微严肃一点,手动建立有规格的储存模块,即同步FIFO.那 ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十六:IIC储存模块
IIC储存器是笔者用来练习精密控时的经典例子.<整合篇>之际,IIC储存器的解释,笔者也自认变态.如今笔者回头望去,笔者也不知道自己当初到底发什么神经,既然将IIC的时序都解释一番.由于开 ...
随机推荐
- HttpURLConnection和HttpClient的区别(转)
HTTP 协议可能是现在 Internet 上使用得最多.最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源.在 JDK 的 java.net 包中已经提供了访问 ...
- 【LSTM】Understanding-LSTMs
http://colah.github.io/posts/2015-08-Understanding-LSTMs/
- 点击除元素以外的任意地方隐藏元素js
比如想实现点击列表弹出筛选器,点击其他任意地方关闭筛选器,如图 该筛选器class名 $(document).click(function () { $(".subMenu").h ...
- 系统安装SQL Sever2000后1433端口未开放,如何打开1433端口的解决方法
这篇文章主要针对Win2003系统安装SQL Sever2000后1433端口未开放,如何打开1433端口的解决方法. 用了几年的Windows2003和SQL Server2000了,不过这个问题倒 ...
- javascript生成m位随机数
根据时间生成m位随机数,最大13位随机数,并且不能保证首位不为0 function ran(m) { m = m > 13 ? 13 : m; var num = new Date().getT ...
- 关于 wsdl2Java 自动生成客户端调取webservice接口
webservice地址:http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl wsdl2Java 自动生成类名: 客户端调 ...
- C#调用Delphi的dll之详解
C#调用Delphi接口方法,有两种解决办法: 一.将Delphi程序编译成一个COM组件,然后在C#里引用COM组件. 二.非托管调用Dephi的DLL文件. 这里我们主要讲解一下第二种方法,讲第二 ...
- xcode 5.1打包iOS 7.1应用问题笔记
XCODE 5.1默认情况下是要求应用都通过64位编译.但是往往有些第三方的类库还是32位.还木有更新64位类库.使得项目编译出错. 解决办法: BuildSetting 的Valid Archite ...
- Linux+Redis实战教程_day03_Redis-set【重点】_有序set(了解)
2.redis-set[重点] Java HashSet 无序,不重复. Redis操作中,涉及到两个大数据集合的并集,交集,差集运算. 赋值: l sadd key values[value1.v ...
- Servlet入门总结及第一个Servlet程序
目录 一了解Servlet的概念 二Servlet技术功能 三 Servlet技术特点 四 Servlet生命周期 五servlet工作过程 六 Servlet与JSP区别 七Servlet代码结构 ...