📄 usb1_pa.v
字号:
///////////////////////////////////////////////////////////////////////// //////// Packet Assembler //////// Assembles Token and Data USB packets //////// //////// Author: Rudolf Usselmann //////// rudi@asics.ws //////// //////// //////// Downloaded from: http://www.opencores.org/cores/usb1_funct///////// ///////////////////////////////////////////////////////////////////////////// //////// Copyright (C) 2000-2002 Rudolf Usselmann //////// www.asics.ws //////// rudi@asics.ws //////// //////// This source file may be used and distributed without //////// restriction provided that this copyright statement is not //////// removed from the file and that any derivative work contains //////// the original copyright notice and the associated disclaimer.//////// //////// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //////// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //////// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //////// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //////// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //////// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //////// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //////// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //////// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //////// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //////// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //////// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //////// POSSIBILITY OF SUCH DAMAGE. //////// /////////////////////////////////////////////////////////////////////////// CVS Log//// $Id: usb1_pa.v,v 1.1.1.1 2002/09/19 12:07:13 rudi Exp $//// $Date: 2002/09/19 12:07:13 $// $Revision: 1.1.1.1 $// $Author: rudi $// $Locker: $// $State: Exp $//// Change History:// $Log: usb1_pa.v,v $// Revision 1.1.1.1 2002/09/19 12:07:13 rudi// Initial Checkin////还有些疑问:rd_next,last,tx_valid_d,tx_valid_last!!!//////////`include "usb1_defines.v"module usb1_pa( clk, rst, // UTMI TX I/F tx_data, tx_valid, tx_valid_last, tx_ready, tx_first, // Protocol Engine Interface send_token, token_pid_sel, send_data, data_pid_sel, // IDMA Interface tx_data_st, rd_next, ep_empty );input clk, rst;// UTMI TX Interfaceoutput [7:0] tx_data;output tx_valid;output tx_valid_last;input tx_ready;output tx_first;// Protocol Engine Interfaceinput send_token;input [1:0] token_pid_sel;input send_data; //事实上这个由idma提供input [1:0] data_pid_sel;// IDMA Interfaceinput [7:0] tx_data_st;output rd_next;input ep_empty;/////////////////////////////////////////////////////////////////////// Local Wires and Registers//parameter [3:0] // synopsys enum state IDLE = 4'b0001, DATA = 4'b0010, CRC1 = 4'b0100, CRC2 = 4'b1000;reg [3:0] /* synopsys enum state */ state, next_state;// synopsys state_vector statereg last;//这个是干什么的??reg rd_next;//不明,可以去查idma模块reg [7:0] token_pid, data_pid; // PIDs from selectorsreg [7:0] tx_data_d;//跟tx_data什么关系?reg [7:0] tx_data_data;//这是干什么的?reg dsel; //用来选tx_spec_data的reg tx_valid_d;//跟tx_valid什么关系?输出的寄存器reg send_token_r;reg [7:0] tx_spec_data;reg crc_sel1, crc_sel2;reg tx_first_r;reg send_data_r;wire crc16_clr;reg [15:0] crc16;wire [15:0] crc16_next;wire [15:0] crc16_rev;reg crc16_add;//这个是一位信号reg send_data_r2;//注意send_data有两级寄存器reg tx_valid_r;//不是有了tx_valid_d作寄存器吗?reg tx_valid_r1;//153行,搞这么多级为了什么??wire zero_length;//wire型/////////////////////////////////////////////////////////////////////// Misc Logic//reg zero_length_r;//零长度寄存器,干什么用的?assign zero_length = ep_empty;//根本不知道它们在干什么!端点空always @(posedge clk or negedge rst) if(!rst) zero_length_r <= #1 1'b0;//复位时,零长寄存器清零 else if(last) zero_length_r <= #1 1'b0;//这个last从哪里来的?状态机产生的 else if(crc16_clr) zero_length_r <= #1 zero_length;//如果清除crc16,那么就把ep_empty的信号写入之always @(posedge clk) //一级缓冲装载 tx_valid_r1 <= #1 tx_valid;//134行always @(posedge clk) //二级缓冲装载 tx_valid_r <= #1 tx_valid_r1;//在做什么!!!你看看上句always @(posedge clk or negedge rst) if(!rst) send_token_r <= #1 1'b0;//复位清零 else if(send_token) send_token_r <= #1 1'b1;//send_token_r缓冲保存send_token else if(tx_ready) send_token_r <= #1 1'b0;//utmi可传输却清零,为什么?表达一种清零关系而已吧?// PID Select握手包装配always @(token_pid_sel) case(token_pid_sel) // synopsys full_case parallel_case 2'd0: token_pid = { ~`USBF_T_PID_ACK, `USBF_T_PID_ACK}; 2'd1: token_pid = { ~`USBF_T_PID_NACK, `USBF_T_PID_NACK}; 2'd2: token_pid = {~`USBF_T_PID_STALL, `USBF_T_PID_STALL}; 2'd3: token_pid = { ~`USBF_T_PID_NYET, `USBF_T_PID_NYET}; endcasealways @(data_pid_sel)//数据包标识符装配 case(data_pid_sel) // synopsys full_case parallel_case 2'd0: data_pid = { ~`USBF_T_PID_DATA0, `USBF_T_PID_DATA0}; 2'd1: data_pid = { ~`USBF_T_PID_DATA1, `USBF_T_PID_DATA1}; 2'd2: data_pid = { ~`USBF_T_PID_DATA2, `USBF_T_PID_DATA2}; 2'd3: data_pid = { ~`USBF_T_PID_MDATA, `USBF_T_PID_MDATA}; endcase// Data path Muxesalways @(send_token or send_token_r or token_pid or tx_data_data)//发送握手包还是数据 if(send_token | send_token_r) tx_data_d = token_pid; else tx_data_d = tx_data_data;//tx_data_data是要传输的数据的二级缓冲always @(dsel or tx_data_st or tx_spec_data)//tx_data_data在tx_data_st和 if(dsel) tx_data_data = tx_spec_data;//tx_spec_data中作选择,由dsel决定,dsel好象是用来选择特殊数据的 else tx_data_data = tx_data_st;//这个进程装载tx_spec_dataalways @(crc_sel1 or crc_sel2 or data_pid or crc16_rev)//如果crc_sel1和crc_sel2均为低,则组装数据包标识符 if(!crc_sel1 & !crc_sel2) tx_spec_data = data_pid;// else if(crc_sel1) tx_spec_data = crc16_rev[15:8]; // CRC 1 else tx_spec_data = crc16_rev[7:0]; // CRC 2assign tx_data = tx_data_d;//这个缓冲关系要留下印象,这可不是什么缓冲关系,引脚直接相连了!!!// TX Valid assignment 看不懂assign tx_valid_last = send_token | last;//这个条件不完全懂,tx_valid_last可去utmi查assign tx_valid = tx_valid_d;//直接相连了,值永远保持一致always @(posedge clk)//只要是有东西要发送了,那么tx_first_r就变高 tx_first_r <= #1 send_token | send_data; assign tx_first = (send_token | send_data) & ! tx_first_r;//有东西要发且tx_first_r为低才可使tx_first为高,可能出现毛刺// CRC Logicalways @(posedge clk) send_data_r <= #1 send_data;always @(posedge clk) send_data_r2 <= #1 send_data_r;assign crc16_clr = send_data & !send_data_r;//清零条件的巧妙 //send_data为高,send_data_r为低清零always @(posedge clk)//设定crc16_add信号,crc16_add究竟是干什么的?可能是表明什么时候需要装配上crc16的 crc16_add <= #1 !zero_length_r & ((send_data_r & !send_data_r2) | (rd_next & !crc_sel1));//搞清last在里面起的关键作用,直接控制了 //zero_length_r的清零与否。 always @(posedge clk) if(crc16_clr) crc16 <= #1 16'hffff;//置位同步,但我担心它的时序.不必担心:) else if(crc16_add) crc16 <= #1 crc16_next;//需要加载crc16usb1_crc16 u1( //难道下一次的crc16码还与本次的有关?想通了,因为crc16可能是要校验多个字节的数据 .crc_in( crc16 ), .din( {tx_data_st[0], tx_data_st[1], tx_data_st[2], tx_data_st[3], tx_data_st[4], tx_data_st[5], tx_data_st[6], tx_data_st[7]} ), .crc_out( crc16_next ) );assign crc16_rev[15] = ~crc16[8];assign crc16_rev[14] = ~crc16[9];assign crc16_rev[13] = ~crc16[10];assign crc16_rev[12] = ~crc16[11];assign crc16_rev[11] = ~crc16[12];assign crc16_rev[10] = ~crc16[13];assign crc16_rev[9] = ~crc16[14];assign crc16_rev[8] = ~crc16[15];assign crc16_rev[7] = ~crc16[0];assign crc16_rev[6] = ~crc16[1];assign crc16_rev[5] = ~crc16[2];assign crc16_rev[4] = ~crc16[3];assign crc16_rev[3] = ~crc16[4];assign crc16_rev[2] = ~crc16[5];assign crc16_rev[1] = ~crc16[6];assign crc16_rev[0] = ~crc16[7];/////////////////////////////////////////////////////////////////////// Transmit/Encode state machine//always @(posedge clk or negedge rst) if(!rst) state <= #1 IDLE;//复位为空闲状态 else state <= #1 next_state;always @(state or send_data or tx_ready or tx_valid_r or zero_length)//由此可见判断总线上的数据是否有效都是用的tx_valid_r begin next_state = state; // Default don't change current state tx_valid_d = 1'b0;//每次开始执行时,给utmi的信号都通报总线上的数据无效 dsel = 1'b0;//用来选tx_spec_data的,每次开始时,都切换为tx_data_st rd_next = 1'b0;//跟idma有关 last = 1'b0;//刚开始时都清零 crc_sel1 = 1'b0; crc_sel2 = 1'b0; case(state) // synopsys full_case parallel_case IDLE: begin if(zero_length & send_data)//要传数据并且零长度,注意:zero_length是直接连到ep_empty的 begin tx_valid_d = 1'b1; dsel = 1'b1;//此时,数据包的data_pid已送出,这个时钟一到,这个data_pid就通过tx_data存到utmi中了 next_state = CRC1;//因为是零长度的数据包,下面自动去crc16模块 end else if(send_data) // Send DATA packet,不是零长度的数据包 begin tx_valid_d = 1'b1; dsel = 1'b1; //这个时候,已发出data_pid next_state = DATA; end end DATA: //切换到该状态时,dsel已自己清零了,会把tx_data_st存到tx_data_d begin if(tx_ready & tx_valid_r)//可以传输并且前一个时钟在总线上的数据有效,注意它用的是tx_valid_r rd_next = 1'b1;//应该是准备读下个数据了 tx_valid_d = 1'b1;//告诉utmi数据有效。有效数据已送出,我总觉得这句和上句该调换位置 if(!send_data & tx_ready & tx_valid_r)//如果数据传输完并且可以传输,而且前一个在总线上的数据有效(何必加这个条件) begin //则开始装配crc dsel = 1'b1; crc_sel1 = 1'b1; next_state = CRC1; end end CRC1: begin dsel = 1'b1; tx_valid_d = 1'b1;//表明数据有效,此时crc1已放到总线上 if(tx_ready)//如果此时可以传输,如果这个条件为真,那么上个时钟发送的data_pid已经送出到主机了,如果是零字节数据,是有点问题!! begin last = 1'b1;//准备传crc2 crc_sel2 = 1'b1;//我认为现在不必将之置为1 next_state = CRC2;//crc1传完,准备切换到crc2 end else//如果此时忙,不能传输,则不跳到下个状态crc2,而继续传输crc1 begin tx_valid_d = 1'b1; crc_sel1 = 1'b1; end end CRC2: begin dsel = 1'b1; crc_sel2 = 1'b1;//到这里时,已将crc2的数据放到tx_data_d中了。这里有疑问:需要的crc数据是放上去了,但是tx_valid_d为零啊!!!有可能被last搞定 if(tx_ready) begin next_state = IDLE;//crc2装载完毕,回到idle状态 end else begin last = 1'b1;//继续传crc2 end end endcase endendmodule
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -