基于FPGA的CAN总线控制器的设计(中)

今天给大侠带来基于FPGA的CAN总线控制器的设计,由于篇幅较长,分三篇。今天带来第二篇,中篇,CAN 通信控制器的具体实现。话不多说,上货。

 

 

 

 

 

导读 

 

 

CAN 总线(Controller Area Network)是控制器局域网的简称,是 20 世纪 80 年代初德国 BOSCH 公司为解决现代汽车中众多的控制与测试仪器之间的数据交换而开发的一种串行数据通信协议。目前,CAN 总线已经被列入 ISO 国际标准,称为 ISO11898。CAN 总线已经成为工业数据通信的主流技术之一。

CAN 总线作为数字式串行通信技术,与其他同类技术相比,在可靠性、实时性和灵活性方面具有独特的技术优势,主要特点如下:

  •  CAN 总线是一种多主总线,总线上任意节点可在任意时刻主动地向网络上其他节点发送信息而不分主次,因此可在各节点之间实现自由通信。

  •  CAN 总线采用非破坏性总线仲裁技术。但多个节点同时向总线发送信息时,优先级低的节点会主动退出发送,而最高优先级的节点可以不受影响地继续传输数据,从而大大节省总线冲突的仲裁时间。即使在网络负载很重的情况下也不会发生网络瘫痪情况。

  • CAN 总线的通信介质可以是双绞线、同轴电缆或光导纤维,选择灵活。

  • CAN 总线的通信速率可达 1Mbit/s(此时通信距离最长为 40 米),通信距离最远可达 10km(速率在 5kbit/s 以下)。

  •  CAN 总线上的节点信息分成不同的优先级,可以满足不同级别的实时要求,高优先级的数据可以在 134μs 内得到传输。

  • CAN 总线通过报文滤波即可实现点对点、一点对多点及全局广播等几种方式传送数据,无需专门的调度。

  •  CAN 总线的数据采用短帧结构,传输时间短,受干扰概率低,具有极好的检错效果。

  • CAN 总线采用 CRC 检验并可提供相应的错误处理功能,保证了数据通信的可靠性。

  • CAN 总线上的器件可被置于无任何内部活动的睡眠方式,相当于未连接到总线上,可以有效降低系统功耗。

 

CAN 总线上的节点在错误严重的情况下具有自动关闭输出的功能,以使总线上其他节点的操作不受影响。CAN 总线卓越的特性、极高的可靠性和独特的设计,特别适合工业过程中监控设备的互连,因此,越来越受到工业界的重视,并被公认为是最有前途的现场总线之一。另外,CAN 总线协议已被国际标准化组织认可,技术比较成熟,控制的芯片已经商品化,性价比高,特别适用于分布式测控系统之间的数通讯。

CAN 总线插卡可以任意插在 PC AT XT 兼容机上,方便地构成分布式监控系统。因此,用 FPGA 实现 CAN 总线通信控制器具有非常重要的应用价值。本篇将通过一个实例讲解利用 FPGA 实现 CAN 总线通信控制器的实现方法。

第二篇内容摘要:本篇会介绍CAN 通信控制器的具体实现,包括顶层控制程序、寄存器控制、 位时序逻辑(Bit Timing Logic)、位数据流处理器(Bit Stream Processor)、CRC 校验 、FIFO等相关内容。

 

基于FPGA的CAN总线控制器的设计(中)_第1张图片

三、CAN 通信控制器的具体实现

 

各模块的组织结构如图 10 所示。

基于FPGA的CAN总线控制器的设计(中)_第2张图片

图 10 程序组织结构

 

3.1 顶层控制程序——TOP

TOP 程序处于整个程序的最顶层,控制其他部分的正常运行。主要程序代码如下:

//连接其他模块//寄存器模块  can_registers i_can_registers (                                .clk(clk_i),                                .rst(rst),                                .cs(cs),                                .we(we),                              ….)                            //连接 Bit Timing Logic 模块  can_btl i_can_btl (                      .clk(clk_i),                      .rst(rst),                      .rx(rx_i),                     …)                   //连接 Bit Streaming Processor 模块  can_bsp i_can_bsp(                      .clk(clk_i),                      .rst(rst),                    …)//选择输出 fifo 或者寄存器中的数据模式  always @ (extended_mode or addr or reset_mode)    begin      if (extended_mode & (~reset_mode) & ((addr >= 8'd16) && (addr <= 8'd28)) | (~extended_mode)        & ((addr >= 8'd20) && (addr <= 8'd29)))        data_out_fifo_selected <= 1'b1;      else        data_out_fifo_selected <= 1'b0;      end       //输出数据    always @ (posedge clk_i)      begin        if (cs & (~we))          begin            if (data_out_fifo_selected)              data_out <=#Tp data_out_fifo;        else            data_out <=#Tp data_out_regs;          end      end// 锁存地址  always @ (negedge clk_i or posedge rst)    begin      if (rst)        addr_latched <= 8'h0;      else if (ale_i)        addr_latched <=#Tp port_0_io;    end    // 产生延迟信号  always @ (posedge clk_i or posedge rst)    begin      if (rst)        begin          wr_i_q <= 1'b0;          rd_i_q <= 1'b0;        end      else        begin          wr_i_q <=#Tp wr_i;          rd_i_q <=#Tp rd_i;        end    end  //组合得到多个信号,如片选、重起等  assign cs = ((wr_i & (~wr_i_q)) | (rd_i & (~rd_i_q))) & cs_can_i;  assign rst = rst_i;  assign we = wr_i;  assign addr = addr_latched;  assign data_in = port_0_io;  assign port_0_io = (cs_can_i & rd_i)? data_out : 8'hz;

 

 

3.2 寄存器控制

这个模块用于完成程序中所有有关寄存器的操作,代码如下:

  always @ (posedge clk)    begin      tx_successful_q <=#Tp tx_successful;      overrun_q <=#Tp overrun;      transmit_buffer_status_q <=#Tp transmit_buffer_status;      info_empty_q <=#Tp info_empty;      error_status_q <=#Tp error_status;      node_bus_off_q <=#Tp node_bus_off;      node_error_passive_q <=#Tp node_error_passive;    end    //模式寄存器  wire [0:0] mode;  wire [4:1] mode_basic;  wire [3:1] mode_ext;  wire receive_irq_en_basic;  wire transmit_irq_en_basic;  wire error_irq_en_basic;  wire overrun_irq_en_basic;    can_register_asyn_syn #(1, 1'h1) MODE_REG0(                                               .data_in(data_in[0]),                                              .data_out(mode[0]),                                              .we(we_mode),                                              .clk(clk),                                              .rst(rst),                                              .rst_sync(set_reset_mode)                                             );                                               can_register_asyn #(4, 0) MODE_REG_BASIC(                                             .data_in(data_in[4:1]),                                            .data_out(mode_basic[4:1]),                                            .we(we_mode),                                            .clk(clk),                                            .rst(rst)                                           );                                             can_register_asyn #(3, 0) MODE_REG_EXT(                                           .data_in(data_in[3:1]),                                          .data_out(mode_ext[3:1]),                                          .we(we_mode & reset_mode),                                          .clk(clk),                                          .rst(rst)                                         );  //命令寄存器  wire [4:0] command;    can_register_asyn_syn #(1, 1'h0) COMMAND_REG0(                                                 .data_in(data_in[0]),                                                .data_out(command[0]),                                                .we(we_command),                                                .clk(clk),                                                .rst(rst),                                                .rst_sync(tx_request & sample_point)                                              );    can_register_asyn_syn #(1, 1'h0) COMMAND_REG1(                                                 .data_in(data_in[1]),                                                .data_out(command[1]),                                                .we(we_command),                                                .clk(clk),                                                .rst(rst),                                                .rst_sync(abort_tx & ~transmitting)                                              );    can_register_asyn_syn #(2, 2'h0) COMMAND_REG(                                                 .data_in(data_in[3:2]),                                                .data_out(command[3:2]),                                                .we(we_command),                                                .clk(clk),                                                .rst(rst),                                                .rst_sync(|command[3:2])                                              );  can_register_asyn_syn #(1, 1'h0) COMMAND_REG4(                                                 .data_in(data_in[4]),                                                .data_out(command[4]),                                                .we(we_command),                                                .clk(clk),                                                .rst(rst),                                                .rst_sync(tx_successful & (~tx_successful_q) | abort_tx)                                                );                                                  assign self_rx_request = command[4] & (~command[0]);  assign clear_data_overrun = command[3];  assign release_buffer = command[2];  assign abort_tx = command[1] & (~command[0]) & (~command[4]);  assign tx_request = command[0] | command[4];    always @ (posedge clk or posedge rst)  begin    if (rst)      single_shot_transmission <= 1'b0;    else if (we_command & data_in[1] & (data_in[1] | data_in[4]))      single_shot_transmission <=#Tp 1'b1;    else if (tx_successful & (~tx_successful_q))      single_shot_transmission <=#Tp 1'b0;  end  //状态寄存器  wire [7:0] status;  assign status[7] = node_bus_off;  assign status[6] = error_status;  assign status[5] = transmit_status;  assign status[4] = receive_status;  assign status[3] = transmission_complete;  assign status[2] = transmit_buffer_status;  assign status[1] = overrun_status;  assign status[0] = receive_buffer_status;    always @ (posedge clk or posedge rst)  begin    if (rst)      transmission_complete <= 1'b1;    else if (tx_successful & (~tx_successful_q) | abort_tx)      transmission_complete <=#Tp 1'b1;    else if (tx_request)      transmission_complete <=#Tp 1'b0;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      transmit_buffer_status <= 1'b1;    else if (tx_request)      transmit_buffer_status <=#Tp 1'b0;    else if (~need_to_tx)      transmit_buffer_status <=#Tp 1'b1;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      overrun_status <= 1'b0;    else if (overrun & (~overrun_q))      overrun_status <=#Tp 1'b1;    else if (clear_data_overrun)      overrun_status <=#Tp 1'b0;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      receive_buffer_status <= 1'b0;    else if (release_buffer)      receive_buffer_status <=#Tp 1'b0;    else if (~info_empty)      receive_buffer_status <=#Tp 1'b1;  end  //总线时序寄存器 1  wire [7:0] bus_timing_0;    can_register #(8) BUS_TIMING_0_REG(                                       .data_in(data_in),                                      .data_out(bus_timing_0),                                      .we(we_bus_timing_0),                                      .clk(clk)                                    );                                      assign baud_r_presc = bus_timing_0[5:0];  assign sync_jump_width = bus_timing_0[7:6];  //总线时序寄存器 2  wire [7:0] bus_timing_1;    can_register #(8) BUS_TIMING_1_REG(                                       .data_in(data_in),                                      .data_out(bus_timing_1),                                      .we(we_bus_timing_1),                                      .clk(clk)                                     );                                       assign time_segment1 = bus_timing_1[3:0];  assign time_segment2 = bus_timing_1[6:4];  assign triple_sampling = bus_timing_1[7];  //错误提示寄存器  can_register_asyn #(8, 96) ERROR_WARNING_REG(                                                 .data_in(data_in),                                                .data_out(error_warning_limit),                                                .we(we_error_warning_limit),                                                .clk(clk),                                                .rst(rst)                                               );                                               //时钟分频寄存器  wire [7:0] clock_divider;  wire clock_off;  wire [2:0] cd;  reg [2:0] clkout_div;  reg [2:0] clkout_cnt;  reg clkout_tmp;  //reg clkout;    can_register #(1) CLOCK_DIVIDER_REG_7(                                         .data_in(data_in[7]),                                        .data_out(clock_divider[7]),                                        .we(we_clock_divider_hi),                                        .clk(clk)                                       );    assign clock_divider[6:4] = 3'h0;    can_register #(1) CLOCK_DIVIDER_REG_3(                                         .data_in(data_in[3]),                                        .data_out(clock_divider[3]),                                        .we(we_clock_divider_hi),                                        .clk(clk)                                       );                                         can_register #(3) CLOCK_DIVIDER_REG_LOW(                                           .data_in(data_in[2:0]),                                          .data_out(clock_divider[2:0]),                                          .we(we_clock_divider_low),                                          .clk(clk)                                        );    assign extended_mode = clock_divider[7];  assign clock_off = clock_divider[3];  assign cd[2:0] = clock_divider[2:0];    always @ (cd)  begin    case (cd) // synopsys_full_case synopsys_paralel_case      3'b000 : clkout_div <= 0;      3'b001 : clkout_div <= 1;      3'b010 : clkout_div <= 2;      3'b011 : clkout_div <= 3;      3'b100 : clkout_div <= 4;      3'b101 : clkout_div <= 5;      3'b110 : clkout_div <= 6;      3'b111 : clkout_div <= 0;    endcase  end    always @ (posedge clk or posedge rst)  begin    if (rst)      clkout_cnt <= 3'h0;    else if (clkout_cnt == clkout_div)      clkout_cnt <=#Tp 3'h0;    else      clkout_cnt <= clkout_cnt + 1'b1;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      clkout_tmp <= 1'b0;    else if (clkout_cnt == clkout_div)      clkout_tmp <=#Tp ~clkout_tmp;  end    always @ (cd or clkout_tmp or clock_off)  begin    if (clock_off)      clkout <=#Tp 1'b1;    else      clkout <=#Tp clkout_tmp;  end    assign clkout = clock_off ? 1'b1 : ((&cd)? clk : clkout_tmp);  //从寄存器中读数据  always @ ( addr or read or extended_mode or mode or bus_timing_0 or bus_timing_1 or clock_divider  or  acceptance_code_0 or acceptance_code_1 or acceptance_code_2 or acceptance_code_3  or  acceptance_mask_0 or acceptance_mask_1 or acceptance_mask_2 or acceptance_mask_3  or  reset_mode or tx_data_0 or tx_data_1 or tx_data_2 or tx_data_3 or tx_data_4 or  tx_data_5 or tx_data_6 or tx_data_7 or tx_data_8 or tx_data_9 or status or  error_warning_limit or rx_err_cnt or tx_err_cnt or irq_en_ext or irq_reg or  mode_ext or  arbitration_lost_capture or rx_message_counter or mode_basic or  error_capture_code  )  begin  if(read) // read  begin    if (extended_mode) // EXTENDED mode (Different register map depends on mode)      begin        case(addr)          8'd0 : data_out_tmp <= {4'b0000, mode_ext[3:1], mode[0]};          8'd1 : data_out_tmp <= 8'h0;          8'd2 : data_out_tmp <= status;          8'd3 : data_out_tmp <= irq_reg;          8'd4 : data_out_tmp <= irq_en_ext;          8'd6 : data_out_tmp <= bus_timing_0;          8'd7 : data_out_tmp <= bus_timing_1;          8'd11 : data_out_tmp <= {3'h0, arbitration_lost_capture[4:0]};          8'd12 : data_out_tmp <= error_capture_code;          8'd13 : data_out_tmp <= error_warning_limit;          8'd14 : data_out_tmp <= rx_err_cnt;          8'd15 : data_out_tmp <= tx_err_cnt;          8'd16 : data_out_tmp <= acceptance_code_0;          8'd17 : data_out_tmp <= acceptance_code_1;          8'd18 : data_out_tmp <= acceptance_code_2;          8'd19 : data_out_tmp <= acceptance_code_3;          8'd20 : data_out_tmp <= acceptance_mask_0;          8'd21 : data_out_tmp <= acceptance_mask_1;          8'd22 : data_out_tmp <= acceptance_mask_2;          8'd23 : data_out_tmp <= acceptance_mask_3;          8'd24 : data_out_tmp <= 8'h0;          8'd25 : data_out_tmp <= 8'h0;          8'd26 : data_out_tmp <= 8'h0;          8'd27 : data_out_tmp <= 8'h0;          8'd28 : data_out_tmp <= 8'h0;          8'd29 : data_out_tmp <= {1'b0, rx_message_counter};          8'd31 : data_out_tmp <= clock_divider;          default: data_out_tmp <= 8'h0;        endcase      end    else // BASIC mode      begin        case(addr)          8'd0 : data_out_tmp <= {3'b001, mode_basic[4:1], mode[0]};          8'd1 : data_out_tmp <= 8'hff;          8'd2 : data_out_tmp <= status;          8'd3 : data_out_tmp <= {4'hf, irq_reg[3:0]};          8'd4 : data_out_tmp <= reset_mode? acceptance_code_0 : 8'hff;          8'd5 : data_out_tmp <= reset_mode? acceptance_mask_0 : 8'hff;          8'd6 : data_out_tmp <= reset_mode? bus_timing_0 : 8'hff;          8'd7 : data_out_tmp <= reset_mode? bus_timing_1 : 8'hff;          8'd10 : data_out_tmp <= reset_mode? 8'hff : tx_data_0;          8'd11 : data_out_tmp <= reset_mode? 8'hff : tx_data_1;          8'd12 : data_out_tmp <= reset_mode? 8'hff : tx_data_2;          8'd13 : data_out_tmp <= reset_mode? 8'hff : tx_data_3;          8'd14 : data_out_tmp <= reset_mode? 8'hff : tx_data_4;          8'd15 : data_out_tmp <= reset_mode? 8'hff : tx_data_5;          8'd16 : data_out_tmp <= reset_mode? 8'hff : tx_data_6;          8'd17 : data_out_tmp <= reset_mode? 8'hff : tx_data_7;          8'd18 : data_out_tmp <= reset_mode? 8'hff : tx_data_8;          8'd19 : data_out_tmp <= reset_mode? 8'hff : tx_data_9;          8'd31 : data_out_tmp <= clock_divider;          default: data_out_tmp <= 8'h0;        endcase      end      end    else      data_out_tmp <= 8'h0;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      data_out <= 0;    else if (read)      data_out <=#Tp data_out_tmp;  end

 

 

 

3.3 位时序逻辑——Bit Timing Logic

位时序逻辑实现 CAN 总线协议中对位同步的有关控制。位时序逻辑监视串行 CAN 总线并处理与总线相关的位时序。它在报文开始发送、总线电平从隐性值跳变到显性值时同步于 CAN总线上的位数据流(硬同步),并在该报文的传送过程中,每遇到一次从隐性值到显性值的跳变沿就进行一次重同步(软同步)。位时序逻辑还提供可编程的时间段来补偿传播延迟时间和相位漂移。主要程序代码如下:

//计数器  always @ (posedge clk or posedge rst)  begin    if (rst)      clk_cnt <= 0;    else if (clk_cnt == (preset_cnt-1))      clk_cnt <=#Tp 0;    else      clk_cnt <=#Tp clk_cnt + 1;  end  //产生定义波特率的一般使能信号  always @ (posedge clk or posedge rst)  begin    if (rst)      clk_en <= 1'b0;    else if (clk_cnt == (preset_cnt-1))      clk_en <=#Tp 1'b1;    else      clk_en <=#Tp 1'b0;  end  //改变状态  assign go_sync = clk_en & (seg2 & (~hard_sync) & (~resync) & ((quant_cnt == time_segment2)));  assign go_seg1 = clk_en & (sync | hard_sync | (resync & seg2 & sync_window) | (resync_latched  & sync_window));  assign go_seg2 = clk_en & (seg1 & (~hard_sync) & (quant_cnt == (time_segment1 + delay)));  //当探测到 SJW 字段的沿时,同步请求被锁存并被执行  always @ (posedge clk or posedge rst)  begin    if (rst)      resync_latched <= 1'b0;    else if (resync & seg2 & (~sync_window))      resync_latched <=#Tp 1'b1;    else if (go_seg1)      resync_latched <= 1'b0;  end//同步的平台或片断  always @ (posedge clk or posedge rst)  begin    if (rst)      sync <= 0;    else if (go_sync)      sync <=#Tp 1'b1;    else if (go_seg1)      sync <=#Tp 1'b0;  end    assign tx_point = go_sync;  //片断 seg1  always @ (posedge clk or posedge rst)  begin    if (rst)      seg1 <= 1;    else if (go_seg1)      seg1 <=#Tp 1'b1;    else if (go_seg2)      seg1 <=#Tp 1'b0;  end  //片断 seg2  always @ (posedge clk or posedge rst)  begin    if (rst)      seg2 <= 0;    else if (go_seg2)      seg2 <=#Tp 1'b1;    else if (go_sync | go_seg1)      seg2 <=#Tp 1'b0;  end  //Quant 计数器  always @ (posedge clk or posedge rst)  begin    if (rst)      quant_cnt <= 0;    else if (go_sync | go_seg1 | go_seg2)      quant_cnt <=#Tp 0;    else if (clk_en)      quant_cnt <=#Tp quant_cnt + 1'b1;  end  //当探测到后沿时,片断 seg1 被延时  begin    if (rst)      delay <= 0;    else if (clk_en & resync & seg1)      delay <=#Tp (quant_cnt > sync_jump_width)? (sync_jump_width + 1) : (quant_cnt + 1);    else if (go_sync | go_seg1)      delay <=#Tp 0;  end  //如果沿出现在这个窗口中,相位的错误将得到完全的补偿  assign sync_window = ((time_segment2 - quant_cnt) < ( sync_jump_width + 1));//数据采样  always @ (posedge clk or posedge rst)  begin    if (rst)      sample <= 2'b11;    else if (clk_en)      sample <= {sample[0], rx};  end//获得使能后,采样完成  always @ (posedge clk or posedge rst)  begin    if (rst)      begin        sampled_bit <= 1;        sampled_bit_q <= 1;        sample_point <= 0;      end    else if (clk_en & (~hard_sync))      begin        if (seg1 & (quant_cnt == (time_segment1 + delay)))          begin            sample_point <=#Tp 1;            sampled_bit_q <=#Tp sampled_bit;              if (triple_sampling)                sampled_bit <=#Tp (sample[0] & sample[1]) | ( sample[0] & rx) | (sample[1] & rx);              else                sampled_bit <=#Tp rx;          end      end        else          sample_point <=#Tp 0;  end  //阻塞同步  always @ (posedge clk or posedge rst)  begin    if (rst)      sync_blocked <=#Tp 1'b0;    else if (clk_en)      begin        if (hard_sync | resync)          sync_blocked <=#Tp 1'b1;        else if (seg2 & quant_cnt == time_segment2)          sync_blocked <=#Tp 1'b0;      end  end//阻塞重同步直到收到开始信号  /* Blocking resynchronization until reception starts (needed because after reset mode exits  we are waiting for  end-of-frame and interframe. No resynchronization is needed meanwhile). */  always @ (posedge clk or posedge rst)  begin    if (rst)      resync_blocked <=#Tp 1'b1;    else if (reset_mode)      resync_blocked <=#Tp 1'b1;    else if (hard_sync)      resync_blocked <=#Tp 1'b0;  end

 

 

3.4 位数据流处理器——Bit Stream Processor

位数据流处理器负责完成程序中所有有关数据的操作。位数据流处理器实际上就是一个序列发生器,它控制发送缓冲器、接收 FIFO 和 CAN 总线之间的数据流,同时它也执行错误检测、仲裁、位填充和 CAN 总线错误处理功能。位数据流处理器程序结构如图 11 所示。

基于FPGA的CAN总线控制器的设计(中)_第3张图片

图 11 位数据流处理器程序结构

 

主要程序代码如下:

//各个数据收发的起始状态//接收数据的 idle 状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_idle <= 1'b0;    else if (reset_mode | go_rx_id1 | error_frame)      rx_idle <=#Tp 1'b0;    else if (go_rx_idle)      rx_idle <=#Tp 1'b1;  end// 接收数据的 id1 状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_id1 <= 1'b0;    else if (reset_mode | go_rx_rtr1 | error_frame)      rx_id1 <=#Tp 1'b0;    else if (go_rx_id1)      rx_id1 <=#Tp 1'b1;  end//接收数据的 rtr1 状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_rtr1 <= 1'b0;    else if (reset_mode | go_rx_ide | error_frame)      rx_rtr1 <=#Tp 1'b0;    else if (go_rx_rtr1)      rx_rtr1 <=#Tp 1'b1;  end//接收数据的 ide 状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_ide <= 1'b0;    else if (reset_mode | go_rx_r0 | go_rx_id2 | error_frame)      rx_ide <=#Tp 1'b0;    else if (go_rx_ide)      rx_ide <=#Tp 1'b1;  end  //接收数据的 id2 状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_id2 <= 1'b0;    else if (reset_mode | go_rx_rtr2 | error_frame)      rx_id2 <=#Tp 1'b0;    else if (go_rx_id2)      rx_id2 <=#Tp 1'b1;  end  //接收数据的 rtr2 状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_rtr2 <= 1'b0;    else if (reset_mode | go_rx_r1 | error_frame)      rx_rtr2 <=#Tp 1'b0;    else if (go_rx_rtr2)      rx_rtr2 <=#Tp 1'b1;  end//接收数据的 r0 状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_r1 <= 1'b0;    else if (reset_mode | go_rx_r0 | error_frame)      rx_r1 <=#Tp 1'b0;    else if (go_rx_r1)      rx_r1 <=#Tp 1'b1;  end  //接收数据的 r0 状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_r0 <= 1'b0;    else if (reset_mode | go_rx_dlc | error_frame)      rx_r0 <=#Tp 1'b0;    else if (go_rx_r0)      rx_r0 <=#Tp 1'b1;  end//接收数据的 dlc 状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_dlc <= 1'b0;    else if (reset_mode | go_rx_data | go_rx_crc | error_frame)      rx_dlc <=#Tp 1'b0;    else if (go_rx_dlc)      rx_dlc <=#Tp 1'b1;  end  //接收数据状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_data <= 1'b0;    else if (reset_mode | go_rx_crc | error_frame)      rx_data <=#Tp 1'b0;    else if (go_rx_data)      rx_data <=#Tp 1'b1;  end  // 接收数据的 crc 状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_crc <= 1'b0;    else if (reset_mode | go_rx_crc_lim | error_frame)      rx_crc <=#Tp 1'b0;    else if (go_rx_crc)      rx_crc <=#Tp 1'b1;  end//接收数据 crc 分隔符状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_crc_lim <= 1'b0;    else if (reset_mode | go_rx_ack | error_frame)      rx_crc_lim <=#Tp 1'b0;    else if (go_rx_crc_lim)      rx_crc_lim <=#Tp 1'b1;  end//接收数据的应答状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_ack <= 1'b0;    else if (reset_mode | go_rx_ack_lim | error_frame)      rx_ack <=#Tp 1'b0;    else if (go_rx_ack)      rx_ack <=#Tp 1'b1;  end  //接收数据分隔符状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_ack_lim <= 1'b0;    else if (reset_mode | go_rx_eof | error_frame)      rx_ack_lim <=#Tp 1'b0;    else if (go_rx_ack_lim)      rx_ack_lim <=#Tp 1'b1;  end//接收数据的帧尾状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_eof <= 1'b0;    else if (go_rx_inter | error_frame | go_overload_frame)      rx_eof <=#Tp 1'b0;    else if (go_rx_eof)      rx_eof <=#Tp 1'b1;  end  //帧间空间状态  always @ (posedge clk or posedge rst)  begin    if (rst)      rx_inter <= 1'b0;    else if (reset_mode | go_rx_idle | go_rx_id1 | go_overload_frame | go_error_frame)      rx_inter <=#Tp 1'b0;    else if (go_rx_inter)      rx_inter <=#Tp 1'b1;  end  // ID 寄存器  always @ (posedge clk or posedge rst)  begin    if (rst)      id <= 0;    else if (sample_point & (rx_id1 | rx_id2) & (~bit_de_stuff))      id <=#Tp {id[27:0], sampled_bit};  end  // rtr1 位  always @ (posedge clk or posedge rst)  begin    if (rst)      rtr1 <= 0;    else if (sample_point & rx_rtr1 & (~bit_de_stuff))      rtr1 <=#Tp sampled_bit;  end  // rtr2 位  always @ (posedge clk or posedge rst)  begin    if (rst)      rtr2 <= 0;    else if (sample_point & rx_rtr2 & (~bit_de_stuff))      rtr2 <=#Tp sampled_bit;  end// ide 位  always @ (posedge clk or posedge rst)  begin    if (rst)      ide <= 0;    else if (sample_point & rx_ide & (~bit_de_stuff))      ide <=#Tp sampled_bit;  end// 获得数据长度  always @ (posedge clk or posedge rst)  begin    if (rst)      data_len <= 0;    else if (sample_point & rx_dlc & (~bit_de_stuff))      data_len <=#Tp {data_len[2:0], sampled_bit};  end  // 获得数据  always @ (posedge clk or posedge rst)  begin    if (rst)      tmp_data <= 0;    else if (sample_point & rx_data & (~bit_de_stuff))      tmp_data <=#Tp {tmp_data[6:0], sampled_bit};  end    always @ (posedge clk or posedge rst)  begin    if (rst)      write_data_to_tmp_fifo <= 0;    else if (sample_point & rx_data & (~bit_de_stuff) & (&bit_cnt[2:0]))      write_data_to_tmp_fifo <=#Tp 1'b1;    else      write_data_to_tmp_fifo <=#Tp 0;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      byte_cnt <= 0;    else if (write_data_to_tmp_fifo)      byte_cnt <=#Tp byte_cnt + 1;    else if (reset_mode | (sample_point & go_rx_crc_lim))      byte_cnt <=#Tp 0;  end    always @ (posedge clk)  begin    if (write_data_to_tmp_fifo)      tmp_fifo[byte_cnt] <=#Tp tmp_data;  end  // CRC 校验数据  always @ (posedge clk or posedge rst)  begin    if (rst)      crc_in <= 0;    else if (sample_point & rx_crc & (~bit_de_stuff))      crc_in <=#Tp {crc_in[13:0], sampled_bit};  end  //计数器  always @ (posedge clk or posedge rst)  begin    if (rst)      bit_cnt <= 0;    else if (go_rx_id1 | go_rx_id2 | go_rx_dlc | go_rx_data | go_rx_crc |      go_rx_ack | go_rx_eof | go_rx_inter | go_error_frame | go_overload_frame)      bit_cnt <=#Tp 0;    else if (sample_point & (~bit_de_stuff))     bit_cnt <=#Tp bit_cnt + 1'b1;  end  //帧尾计数器  always @ (posedge clk or posedge rst)  begin    if (rst)      eof_cnt <= 0;    else if (sample_point)      begin        if (reset_mode | go_rx_inter | go_error_frame | go_overload_frame)          eof_cnt <=#Tp 0;        else if (rx_eof)          eof_cnt <=#Tp eof_cnt + 1'b1;      end  end  // 使能位填充  always @ (posedge clk or posedge rst)  begin    if (rst)      bit_stuff_cnt_en <= 1'b0;    else if (bit_de_stuff_set)      bit_stuff_cnt_en <=#Tp 1'b1;    else if (bit_de_stuff_reset)      bit_stuff_cnt_en <=#Tp 1'b0;  end//位填充计数器  always @ (posedge clk or posedge rst)  begin    if (rst)      bit_stuff_cnt <= 1;    else if (bit_de_stuff_reset)      bit_stuff_cnt <=#Tp 1;    else if (sample_point & bit_stuff_cnt_en)      begin        if (bit_stuff_cnt == 5)          bit_stuff_cnt <=#Tp 1;        else if (sampled_bit == sampled_bit_q)          bit_stuff_cnt <=#Tp bit_stuff_cnt + 1'b1;        else          bit_stuff_cnt <=#Tp 1;      end  end  // 发送数据的使能位填充  always @ (posedge clk or posedge rst)  begin    if (rst)      bit_stuff_cnt_tx_en <= 1'b0;    else if (bit_de_stuff_set & transmitting)      bit_stuff_cnt_tx_en <=#Tp 1'b1;    else if (bit_de_stuff_reset)      bit_stuff_cnt_tx_en <=#Tp 1'b0;  end//发送数据的位填充计数  always @ (posedge clk or posedge rst)  begin    if (rst)      bit_stuff_cnt_tx <= 1;    else if (bit_de_stuff_reset)      bit_stuff_cnt_tx <=#Tp 1;    else if (tx_point_q & bit_stuff_cnt_en)      begin        if (bit_stuff_cnt_tx == 5)          bit_stuff_cnt_tx <=#Tp 1;        else if (tx == tx_q)          bit_stuff_cnt_tx <=#Tp bit_stuff_cnt_tx + 1'b1;        else          bit_stuff_cnt_tx <=#Tp 1;      end  end    assign bit_de_stuff = bit_stuff_cnt == 5;  assign bit_de_stuff_tx = bit_stuff_cnt_tx == 5;//位填充错误  assign stuff_err = sample_point & bit_stuff_cnt_en & bit_de_stuff & (sampled_bit ==  sampled_bit_q);//产生延迟信号  always @ (posedge clk)  begin    reset_mode_q <=#Tp reset_mode;    node_bus_off_q <=#Tp node_bus_off;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      crc_enable <= 1'b0;    else if (go_crc_enable)      crc_enable <=#Tp 1'b1;    else if (reset_mode | rst_crc_enable)      crc_enable <=#Tp 1'b0;  end  //CRC 校验错误  always @ (posedge clk or posedge rst)  begin    if (rst)      crc_err <= 1'b0;    else if (go_rx_ack)      crc_err <=#Tp crc_in != calculated_crc;    else if (reset_mode | error_frame_ended)      crc_err <=#Tp 1'b0;  end  // 一般错误的条件  assign form_err = sample_point & ( ((~bit_de_stuff) & rx_ide & sampled_bit & (~rtr1)) |  (rx_crc_lim & (~sampled_bit)) | (rx_ack_lim & (~sampled_bit)) | ((eof_cnt < 6) & rx_eof &  (~sampled_bit) & (~tx_state) ) | (& rx_eof & (~sampled_bit) & tx_state));    always @ (posedge clk or posedge rst)  begin    if (rst)      ack_err_latched <= 1'b0;    else if (reset_mode | error_frame_ended | go_overload_frame)      ack_err_latched <=#Tp 1'b0;    else if (ack_err)      ack_err_latched <=#Tp 1'b1;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      bit_err_latched <= 1'b0;    else if (reset_mode | error_frame_ended | go_overload_frame)      bit_err_latched <=#Tp 1'b0;    else if (bit_err)      bit_err_latched <=#Tp 1'b1;  end  //规则 5  assign rule5 = (~node_error_passive) & bit_err & (error_frame & (error_cnt1 < 7) |  overload_frame & (overload_cnt1 < 7) );  //规则 3  always @ (posedge clk or posedge rst)  begin    if (rst)      rule3_exc1_1 <= 1'b0;    else if (reset_mode | error_flag_over | rule3_exc1_2)      rule3_exc1_1 <=#Tp 1'b0;    else if (transmitter & node_error_passive & ack_err)      rule3_exc1_1 <=#Tp 1'b1;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      rule3_exc1_2 <= 1'b0;    else if (reset_mode | error_flag_over)      rule3_exc1_2 <=#Tp 1'b0;    else if (rule3_exc1_1)      rule3_exc1_2 <=#Tp 1'b1;    else if ((error_cnt1 < 7) & sample_point & (~sampled_bit))      rule3_exc1_2 <=#Tp 1'b0;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      rule3_exc2 <= 1'b0;    else if (reset_mode | error_flag_over)      rule3_exc2 <=#Tp 1'b0;    else if (transmitter & stuff_err & arbitration_field & sample_point & tx & (~sampled_bit))      rule3_exc2 <=#Tp 1'b1;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      stuff_err_latched <= 1'b0;    else if (reset_mode | error_frame_ended | go_overload_frame)      stuff_err_latched <=#Tp 1'b0;    else if (stuff_err)      stuff_err_latched <=#Tp 1'b1;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      form_err_latched <= 1'b0;    else if (reset_mode | error_frame_ended | go_overload_frame)      form_err_latched <=#Tp 1'b0;    else if (form_err)      form_err_latched <=#Tp 1'b1;  end  //接收数据的 CRC 校验can_crc i_can_crc_rx(                      .clk(clk),                      .data(sampled_bit),                      .enable(crc_enable & sample_point & (~bit_de_stuff)),                      .initialize(go_crc_enable),                      .crc(calculated_crc)                    );                    assign no_byte0 = rtr1 | (data_len<1);assign no_byte1 = rtr1 | (data_len<2);// 接收数据 FIFO 的写使能  always @ (posedge clk or posedge rst)  begin    if (rst)      wr_fifo <= 1'b0;    else if (reset_wr_fifo)      wr_fifo <=#Tp 1'b0;    else if (go_rx_inter & id_ok & (~error_frame_ended) & ((~tx_state) | self_rx_request))      wr_fifo <=#Tp 1'b1;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      header_cnt <= 0;    else if (reset_wr_fifo)      header_cnt <=#Tp 0;    else if (wr_fifo & storing_header)      header_cnt <=#Tp header_cnt + 1;  end  //数据计数器  always @ (posedge clk or posedge rst)  begin    if (rst)      data_cnt <= 0;    else if (reset_wr_fifo)      data_cnt <=#Tp 0;    else if (wr_fifo)      data_cnt <=#Tp data_cnt + 1;  end  // 数据的合成并保存到 FIFO 中  always @ (extended_mode or ide or data_cnt or header_cnt or header_len or  storing_header or id or rtr1 or rtr2 or data_len or  tmp_fifo[0] or tmp_fifo[2] or tmp_fifo[4] or tmp_fifo[6] or  tmp_fifo[1] or tmp_fifo[3] or tmp_fifo[5] or tmp_fifo[7])  begin    if (storing_header)      begin        if (extended_mode) // extended mode          begin            if (ide) // extended format              begin                case (header_cnt) // synthesis parallel_case                3'h0 : data_for_fifo <= {1'b1, rtr2, 2'h0, data_len};                3'h1 : data_for_fifo <= id[28:21];                3'h2 : data_for_fifo <= id[20:13];                3'h3 : data_for_fifo <= id[12:5];                3'h4 : data_for_fifo <= {id[4:0], 3'h0};                default: data_for_fifo <= 0;                endcase              end              else // standard format                begin                  case (header_cnt) // synthesis parallel_case                  3'h0 : data_for_fifo <= {1'b0, rtr1, 2'h0, data_len};                  3'h1 : data_for_fifo <= id[10:3];                  3'h2 : data_for_fifo <= {id[2:0], 5'h0};                  default: data_for_fifo <= 0;                  endcase                end              end          else // normal mode            begin              case (header_cnt) // synthesis parallel_case              3'h0 : data_for_fifo <= id[10:3];              3'h1 : data_for_fifo <= {id[2:0], rtr1, data_len};              default: data_for_fifo <= 0;              endcase            end      end    else      data_for_fifo <= tmp_fifo[data_cnt-header_len];  end  // 传输错误帧  always @ (posedge clk or posedge rst)  begin    if (rst)      error_frame <= 1'b0;    else if (reset_mode | error_frame_ended | go_overload_frame)      error_frame <=#Tp 1'b0;    else if (go_error_frame)      error_frame <=#Tp 1'b1;  end    always @ (posedge clk)  begin    if (sample_point)      error_frame_q <=#Tp error_frame;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      error_cnt1 <= 1'b0;    else if (reset_mode | error_frame_ended | go_error_frame | go_overload_frame)      error_cnt1 <=#Tp 1'b0;    else if (error_frame & tx_point & (error_cnt1 < 7))      error_cnt1 <=#Tp error_cnt1 + 1'b1;  end    assign error_flag_over = ((~node_error_passive) & sample_point & (error_cnt1 == 7) |  node_error_passive & sample_point & (passive_cnt == 5)) & (~enable_error_cnt2);    always @ (posedge clk or posedge rst)  begin    if (rst)      error_flag_over_blocked <= 1'b0;    else if (reset_mode | error_frame_ended | go_error_frame | go_overload_frame)      error_flag_over_blocked <=#Tp 1'b0;    else if (error_flag_over)      error_flag_over_blocked <=#Tp 1'b1;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      enable_error_cnt2 <= 1'b0;    else if (reset_mode | error_frame_ended | go_error_frame | go_overload_frame)      enable_error_cnt2 <=#Tp 1'b0;    else if (error_frame & (error_flag_over & sampled_bit))      enable_error_cnt2 <=#Tp 1'b1;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      error_cnt2 <= 0;    else if (reset_mode | error_frame_ended | go_error_frame | go_overload_frame)      error_cnt2 <=#Tp 0;    else if (enable_error_cnt2 & tx_point)      error_cnt2 <=#Tp error_cnt2 + 1'b1;  end    always @ (posedge clk or posedge rst)  begin    if (rst)      delayed_dominant_cnt <= 0;    else if (reset_mode | enable_error_cnt2 | go_error_frame | enable_overload_cnt2 |      go_overload_frame)      delayed_dominant_cnt <=#Tp 0;    else if (sample_point & (~sampled_bit) & ((error_cnt1 == 7) | (overload_cnt1 == 7)))      delayed_dominant_cnt <=#Tp delayed_dominant_cnt + 1'b1;  end  //被动计数  always @ (posedge clk or posedge rst)  begin    if (rst)      passive_cnt <= 0;    else if (reset_mode | error_frame_ended | go_error_frame | go_overload_frame)      passive_cnt <=#Tp 0;    else if (sample_point & (passive_cnt < 5))      begin        if (error_frame_q & (~enable_error_cnt2) & (sampled_bit == sampled_bit_q))          passive_cnt <=#Tp passive_cnt + 1'b1;        else          passive_cnt <=#Tp 0;      end  end  // 传输超载帧always @ (posedge clk or posedge rst)beginif (rst)overload_frame <= 1'b0;else if (reset_mode | overload_frame_ended | go_error_frame)overload_frame <=#Tp 1'b0;else if (go_overload_frame)overload_frame <=#Tp 1'b1;endalways @ (posedge clk or posedge rst)beginif (rst)overload_cnt1 <= 1'b0;else if (reset_mode | overload_frame_ended | go_error_frame | go_overload_frame)overload_cnt1 <=#Tp 1'b0;else if (overload_frame & tx_point & (overload_cnt1 < 7))overload_cnt1 <=#Tp overload_cnt1 + 1'b1;endassign overload_flag_over = sample_point & (overload_cnt1 == 7) & (~enable_overload_cnt2);always @ (posedge clk or posedge rst)beginif (rst)enable_overload_cnt2 <= 1'b0;else if (reset_mode | overload_frame_ended | go_error_frame | go_overload_frame)enable_overload_cnt2 <=#Tp 1'b0;else if (overload_frame & (overload_flag_over & sampled_bit))enable_overload_cnt2 <=#Tp 1'b1;endalways @ (posedge clk or posedge rst)beginif (rst)overload_cnt2 <= 0;else if (reset_mode | overload_frame_ended | go_error_frame | go_overload_frame)overload_cnt2 <=#Tp 0;else if (enable_overload_cnt2 & tx_point)overload_cnt2 <=#Tp overload_cnt2 + 1'b1;endalways @ (posedge clk or posedge rst)beginif (rst)overload_frame_blocked <= 0;else if (reset_mode | go_error_frame | go_rx_id1)overload_frame_blocked <=#Tp 0;else if (go_overload_frame & overload_frame) // This is a second sequentialoverloadoverload_frame_blocked <=#Tp 1'b1;endassign send_ack = (~tx_state) & rx_ack & (~err) & (~listen_only_mode);always @ (posedge clk or posedge rst)beginif (rst)tx <= 1'b1;else if (reset_mode) // Resettx <=#Tp 1'b1;else if (tx_point)beginif (tx_state) // 传输报文tx <=#Tp ((~bit_de_stuff_tx) & tx_bit) | (bit_de_stuff_tx & (~tx_q));else if (send_ack) // 应答tx <=#Tp 1'b0;else if (overload_frame) //传输超载帧beginif (overload_cnt1 < 6)tx <=#Tp 1'b0;elsetx <=#Tp 1'b1;endelse if (error_frame) // 传输错误帧beginif (error_cnt1 < 6)beginif (node_error_passive)tx <=#Tp 1'b1;elsetx <=#Tp 1'b0;endelsetx <=#Tp 1'b1;endelsetx <=#Tp 1'b1;endendalways @ (posedge clk)beginif (tx_point)tx_q <=#Tp tx & (~go_early_tx_latched);end//延迟发送数据always @ (posedge clk)begintx_point_q <=#Tp tx_point;end

 

 

3.5 CRC 校验

CAN 节点中设有错误检测、标定和自检等措施。检测错误包括多种方式,其中最常用、最有效的一种是 CRC 校验。CRC 序列由循环冗余校验码求得的帧检查序组成。为实现 CRC 计算,被除的多项式系数由包括帧起始、仲裁字段、控制字段、数据字段在内的无填充位数据流给出,其 15 个最低位的系数为 0。

此多项式被发生器产生的下列多项式除(系数为模 2 运算):1 3478101415XXXXXXX +++++++该多项式除法的余数即为发向总线的 CRC 序列。为完成此运算,可以使用一个 15 位的移位寄存器 CRC-RG(14:0)。被除多项式位数据流由帧起始到数据字段结束的无填充序列给定,如果以 NXTBIT 标记该位数据流的下一位,则 CRC 序列可以用如下的方式求得:

CRC-RG=0 //初始化移位寄存器REPEATCRCNXT = NXTBIT EXOR CRC-RG(14);CRC-RG(14:1) = CRC-RG(13:0) //寄存器左移一位CRC-RG(0) = 0;IF CRCNXT THENCRC-RG(14:0) = CRC-RG(14:0) EXOR (4599H)END IFUNTIL(CRC 序列开始或者存在一个出错状态)

 

完成数据 CRC 校验的主要代码如下:

  assign crc_next = data ^ crc[14];  assign crc_tmp = {crc[13:0], 1'b0};  //CRC 校验  always @ (posedge clk)  begin    if(initialize)      crc <= #Tp 0;    else if (enable)      begin        if (crc_next)          crc <= #Tp crc_tmp ^ 15'h4599;        else          crc <= #Tp crc_tmp;      end  end

 

 

3.6 FIFO

为实现数据的快速交换,使用了 FIFO,代码如下:

assign write_length_info = (~wr) & wr_q;//延迟写信号  always @ (posedge clk or posedge rst)  begin    if (rst)      wr_q <= 0;    else if (reset_mode)      wr_q <=#Tp 0;    else      wr_q <=#Tp wr;  end  // 数据长度计数器  always @ (posedge clk or posedge rst)  begin    if (rst)      len_cnt <= 0;    else if (reset_mode | write_length_info)      len_cnt <=#Tp 1'b0;    else if (wr & (~fifo_full))      len_cnt <=#Tp len_cnt + 1'b1;  end// 写信息指针  always @ (posedge clk or posedge rst)  begin    if (rst)      wr_info_pointer <= 0;    else if (reset_mode)      wr_info_pointer <=#Tp 0;    else if (write_length_info & (~info_full))      wr_info_pointer <=#Tp wr_info_pointer + 1'b1;  end  //读信息指针  always @ (posedge clk or posedge rst)  begin    if (rst)      rd_info_pointer <= 0;    else if (reset_mode)      rd_info_pointer <=#Tp 0;    else if (release_buffer & (~fifo_empty))      rd_info_pointer <=#Tp rd_info_pointer + 1'b1;  end// 读指针  always @ (posedge clk or posedge rst)  begin    if (rst)      rd_pointer <= 0;    else if (release_buffer & (~fifo_empty))      rd_pointer <=#Tp rd_pointer + length_info;    else if (reset_mode)      rd_pointer <=#Tp 0;  end  // 写指针  always @ (posedge clk or posedge rst)  begin    if (rst)      wr_pointer <= 0;    else if (wr & (~fifo_full))      wr_pointer <=#Tp wr_pointer + 1'b1;    else if (reset_mode)      wr_pointer <=#Tp 0;  end//锁存  always @ (posedge clk or posedge rst)  begin    if (rst)      latch_overrun <= 0;    else if (reset_mode | write_length_info)      latch_overrun <=#Tp 0;    else if (wr & fifo_full)      latch_overrun <=#Tp 1'b1;  end  //统计在 FIFO 中的数据  always @ (posedge clk or posedge rst)  begin    if (rst)      fifo_cnt <= 0;    else if (wr & (~release_buffer) & (~fifo_full))      fifo_cnt <=#Tp fifo_cnt + 1'b1;    else if ((~wr) & release_buffer & (~fifo_empty))      fifo_cnt <=#Tp fifo_cnt - length_info;    else if (wr & release_buffer & (~fifo_full) & (~fifo_empty))      fifo_cnt <=#Tp fifo_cnt - length_info + 1'b1;    else if (reset_mode)      fifo_cnt <=#Tp 0;  end    assign fifo_full = fifo_cnt == 64;  assign fifo_empty = fifo_cnt == 0;  //统计在 length_fifo 和 overrun_info fifo 中的数据  always @ (posedge clk or posedge rst)  begin    if (rst)      info_cnt <= 0;    else if (write_length_info ^ release_buffer)      begin        if (release_buffer & (~info_empty))          info_cnt <=#Tp info_cnt - 1'b1;        else if (write_length_info & (~info_full))          info_cnt <=#Tp info_cnt + 1'b1;      end  end    assign info_full = info_cnt == 64;  assign info_empty = info_cnt == 0;//选择用来读数据的 FIFO 的地址  always @ (extended_mode or rd_pointer or addr)  begin    if (extended_mode) // extended mode      begin        read_address <= rd_pointer + (addr - 8'd16);      end    else // normal mode      begin        read_address <= rd_pointer + (addr - 8'd20);      end  end    always @ (posedge clk)  begin    if (wr & (~fifo_full))      fifo[wr_pointer] <=#Tp data_in;  end  //从 FIFO 中读数据  assign data_out = fifo[read_address];  //写到 length_fifo  always @ (posedge clk)  begin    if (write_length_info & (~info_full))      length_fifo[wr_info_pointer] <=#Tp len_cnt;  end// 读 length_fifo 中的数据  assign length_info = length_fifo[rd_info_pointer];// overrun_info  always @ (posedge clk)  begin    if (write_length_info & (~info_full))      overrun_info[wr_info_pointer] <=#Tp latch_overrun | (wr & fifo_full);  end// 读取 overrun  assign overrun = overrun_info[rd_info_pointer]

 

 

本篇到此结束,下一篇带来基于FPGA的CAN总线控制器的设计(下),会介绍程序的仿真与测试以及总结等相关内容。

 

 

END

 

后续会持续更新,带来Vivado、 ISE、Quartus II 、candence等安装相关设计教程,学习资源、项目资源、好文推荐等,希望大侠持续关注。

大侠们,江湖偌大,继续闯荡,愿一切安好,有缘再见!

 

 

 

 

 

精彩推荐

 

 

 

 

毕设:基于FPGA的FIR数字滤波器设计

FPGA 高级设计:时序分析和收敛

在word文档中添加“原汁原味”代码

FPGA工程师就业班,9月份开课!

你可能感兴趣的:(FPGA项目开发经验分享,fpga,can总线)