设计AI芯片架构的入门 研究生入行数字芯片设计、验证的项目 opentitan

前言

这几年芯片设计行业在国内像坐过山车。时而高亢,时而低潮。最近又因为AI的热潮开始high起来。到底芯片行业的规律是如何?

我谈谈自己观点:芯片设计是“劳动密集型”行业。

“EDA和工具高度标准化和代工厂的工艺标准化之后,芯片设计就变成了“劳动密集型”工作,这也是美国很长一段时间几乎要放弃芯片设计行业的技术背景。当然美国国内也没有这么多EE工程师供应。要跟制造业一样转移给全球各地。”

一家之言,大家看看笑笑就可以了。

我们国内背景是研究生大量扩招,又什么学硕/专硕、全日/非全,品种很齐。

国外研究生、美国、欧洲硕士阶段都是授课型教育,多多益善。国内现在也是。

这给研究生门很大困惑。

为什么呢?缺项目实践。

1 我一贯的观点是设计芯片和FPGA是一个技术,希望FPGA学习者也关注这方面技术。

2 AI时代入门芯片设计要先学好设计和验证,在学习架构设计

所以,本人尝试在知乎开这个栏目,提供一些稍微实战项目。一方面讲解、一方面跟各位互动。

无论是本专业科班学生还是非科班学习,也无论是你已经就业想转行,都可以参考上面的项目来学习。

网上有opentitan有介绍,让我来做第一人,来详细介绍这个项目如何学习和吃下来。

芯片设计、验证一体化项目opentitan学习

关于芯片设计、验证的入门学习书籍、以及一些小练习网上有很多书籍、视频、也有培训公司在做这方面工作。感谢他们在目前中国芯片行业急需工程师背景下所做的努力。网上做芯片验证培训的路科,我就觉得很好。我们大学这方面教学太缺了。

掌握基础知识如何通过项目提升,可能需要的时间更长,也更困难。

opentitan就是这样一个设计、验证的综合性项目。

虽然他是Google公司联合一些半导体公司以RISC-V SOC和安全芯片为样本来打造,但是完全不影响我们把它作为我们入门芯片设计和验证后的实战项目。如果要流片,你也可以采用openLane工具链来生成版图。

1 Opentitan项目介绍

才看到这个项目肯定会很懵,就让我们一点一点吃透这个项目。

除了在github开源了全部项目代码之外,我们可以通过文档学习到很多。

前几年国内成立太多芯片设计公司,建议补课。

2 芯片设计的流程(前端)

1 设计验证

opentitan.org/book/doc/

2 形式验证

opentitan.org/book/doc/

3 创建测试软件(SOC需要)

opentitan.org/book/doc/

4 文档创建

opentitan.org/book/doc/

5 FPGA 验证

opentitan.org/book/doc/

3 Opentitan芯片的设计验证

本篇文章就暂时从这款RISC-V芯片的设计和验证开始,看看我们改如何学习和下手这个项目。

简单来讲,SOC芯片就是 CPU core和 外围电路 加 总线 连接起来的功能模块。

先看看整体模块构成

opentitan整体模块构成

可见 这是一个标准的SOC芯片。 还比较复杂,跟商业芯片业差不了太多。

指令集支持RV32IMCB(32位整数指令集 M 整型乘除法扩展 C 压缩指令集扩展 B 位操作扩展 )大概相当于ARM cortex-M3的水平吧。

关于core看这个文档:

Introduction to Ibex


总线看这个文档:

是SiFive公司的。前几年据说INTEL要收购SiFive,没有成功。芯片行业江湖爱恨情仇每天都在上演,不亚于电视连续剧。

我相信,各位跟关注外围模块的设计验证,这是大家入行最开始干的工作:

4 UART 外围电路模块的设计和验证

本篇文章就以这个最简单的UART模块讲解一下设计和验证的概述,详细在后面文章讲解。目前你暂时没有system verilo和UVM基础暂时也没关系,我带着你先入门再详细去学习这方面知识。

这方面我上面提的路科验证就可以。

设计:

一般芯片采用的verilog。这个项目采用的是system verilog 作为设计语言,但是也有自己的规范:


各位到自己的公司也会有各个公司的设计语言规范。

A template that demonstrates many of the items is given below.

Template:

// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// One line description of the module

module my_module #(
parameter Width = 80,
parameter Height = 24
) (
input clk_i,
input rst_ni,
input req_valid_i,
input [Width-1:0] req_data_i,
output req_ready_o,

);

logic [Width-1:0] req_data_masked;

submodule u_submodule (
.clk_i,
.rst_ni,
.req_valid_i,
.req_data_i (req_data_masked),
.req_ready_o(req_ready),

);

always_comb begin
req_data_masked = req_data_i;
case (fsm_state_q)
ST_IDLE: begin
req_data_masked = req_data_i & MASK_IDLE;

end

endmodule


另外 高度依赖Linting

“Linting is a productivity tool for designers to quickly find typos and bugs at the time when the RTL is written. Running lint is important when using SystemVerilog, a weakly-typed language, unlike other hardware description languages. We consider linting to be critical for conformance to our goals of high quality designs”

因为system verilog写起爽,可能其他工具就要跟上。

5 UART功能框图和代码

UART功能框图

5.1 uart模块的TOP

// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Description: UART top level wrapper file

`include “prim_assert.sv”

module uart
import uart_reg_pkg:😗;
#(
parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1’b1}}
) (
input clk_i,
input rst_ni,

// Bus Interface
input tlul_pkg::tl_h2d_t tl_i,
output tlul_pkg::tl_d2h_t tl_o,

// Alerts
input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o,

// Generic IO
input cio_rx_i,
output logic cio_tx_o,
output logic cio_tx_en_o,

// Interrupts
output logic intr_tx_watermark_o ,
output logic intr_rx_watermark_o ,
output logic intr_tx_empty_o ,
output logic intr_rx_overflow_o ,
output logic intr_rx_frame_err_o ,
output logic intr_rx_break_err_o ,
output logic intr_rx_timeout_o ,
output logic intr_rx_parity_err_o
);

logic [NumAlerts-1:0] alert_test, alerts;
uart_reg2hw_t reg2hw;
uart_hw2reg_t hw2reg;

uart_reg_top u_reg (
.clk_i,
.rst_ni,
.tl_i,
.tl_o,
.reg2hw,
.hw2reg,
// SEC_CM: BUS.INTEGRITY
.intg_err_o (alerts[0]),
.devmode_i (1’b1)
);

uart_core uart_core (
.clk_i,
.rst_ni,
.reg2hw,
.hw2reg,

.rx    (cio_rx_i   ),
.tx    (cio_tx_o   ),

.intr_tx_watermark_o,
.intr_rx_watermark_o,
.intr_tx_empty_o,
.intr_rx_overflow_o,
.intr_rx_frame_err_o,
.intr_rx_break_err_o,
.intr_rx_timeout_o,
.intr_rx_parity_err_o

);

// Alerts
assign alert_test = {
reg2hw.alert_test.q &
reg2hw.alert_test.qe
};

for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_tx
prim_alert_sender #(
.AsyncOn(AlertAsyncOn[i]),
.IsFatal(1’b1)
) u_prim_alert_sender (
.clk_i,
.rst_ni,
.alert_test_i ( alert_test[i] ),
.alert_req_i ( alerts[0] ),
.alert_ack_o ( ),
.alert_state_o ( ),
.alert_rx_i ( alert_rx_i[i] ),
.alert_tx_o ( alert_tx_o[i] )
);
end

// always enable the driving out of TX
assign cio_tx_en_o = 1’b1;

// Assert Known for outputs
ASSERT(TxEnIsOne_A, cio_tx_en_o === 1'b1) ASSERT_KNOWN(TxKnown_A, cio_tx_o, clk_i, !rst_ni || !cio_tx_en_o)

// Assert Known for alerts
`ASSERT_KNOWN(AlertsKnown_A, alert_tx_o)

// Assert Known for interrupts
ASSERT_KNOWN(TxWatermarkKnown_A, intr_tx_watermark_o) ASSERT_KNOWN(RxWatermarkKnown_A, intr_rx_watermark_o)
ASSERT_KNOWN(TxEmptyKnown_A, intr_tx_empty_o) ASSERT_KNOWN(RxOverflowKnown_A, intr_rx_overflow_o)
ASSERT_KNOWN(RxFrameErrKnown_A, intr_rx_frame_err_o) ASSERT_KNOWN(RxBreakErrKnown_A, intr_rx_break_err_o)
ASSERT_KNOWN(RxTimeoutKnown_A, intr_rx_timeout_o) ASSERT_KNOWN(RxParityErrKnown_A, intr_rx_parity_err_o)

// Alert assertions for reg_we onehot check
`ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg, alert_tx_o[0])
endmodule


代码下面是用于形式验证,暂时不管,可见核心是uart_core:

其中.reg2hw .hw2reg

  // Register -> HW type
typedef struct packed {
uart_reg2hw_intr_state_reg_t intr_state; // [126:119]
uart_reg2hw_intr_enable_reg_t intr_enable; // [118:111]
uart_reg2hw_intr_test_reg_t intr_test; // [110:95]
uart_reg2hw_alert_test_reg_t alert_test; // [94:93]
uart_reg2hw_ctrl_reg_t ctrl; // [92:68]
uart_reg2hw_status_reg_t status; // [67:56]
uart_reg2hw_rdata_reg_t rdata; // [55:47]
uart_reg2hw_wdata_reg_t wdata; // [46:38]
uart_reg2hw_fifo_ctrl_reg_t fifo_ctrl; // [37:27]
uart_reg2hw_ovrd_reg_t ovrd; // [26:25]
uart_reg2hw_timeout_ctrl_reg_t timeout_ctrl; // [24:0]
} uart_reg2hw_t;


  // HW -> register type
typedef struct packed {
uart_hw2reg_intr_state_reg_t intr_state; // [64:49]
uart_hw2reg_status_reg_t status; // [48:43]
uart_hw2reg_rdata_reg_t rdata; // [42:35]
uart_hw2reg_fifo_ctrl_reg_t fifo_ctrl; // [34:28]
uart_hw2reg_fifo_status_reg_t fifo_status; // [27:16]
uart_hw2reg_val_reg_t val; // [15:0]
} uart_hw2reg_t;

hw就是我们UART的硬件实现事宜叫hw。

及硬件电路与寄存器之间的关系

5.2 core代码

module uart_core (
input clk_i,
input rst_ni,

input uart_reg_pkg::uart_reg2hw_t reg2hw,
output uart_reg_pkg::uart_hw2reg_t hw2reg,

input rx,
output logic tx,

output logic intr_tx_watermark_o,
output logic intr_rx_watermark_o,
output logic intr_tx_empty_o,
output logic intr_rx_overflow_o,
output logic intr_rx_frame_err_o,
output logic intr_rx_break_err_o,
output logic intr_rx_timeout_o,
output logic intr_rx_parity_err_o
);

import uart_reg_pkg:😗;

localparam int NcoWidth = $bits(reg2hw.ctrl.nco.q);

logic [15:0] rx_val_q;
logic [7:0] uart_rdata;
logic tick_baud_x16, rx_tick_baud;
logic [5:0] tx_fifo_depth, rx_fifo_depth;
logic [5:0] rx_fifo_depth_prev_q;
logic [23:0] rx_timeout_count_d, rx_timeout_count_q, uart_rxto_val;
logic rx_fifo_depth_changed, uart_rxto_en;
logic tx_enable, rx_enable;
logic sys_loopback, line_loopback, rxnf_enable;
logic uart_fifo_rxrst, uart_fifo_txrst;
logic [2:0] uart_fifo_rxilvl;
logic [1:0] uart_fifo_txilvl;
logic ovrd_tx_en, ovrd_tx_val;
logic [7:0] tx_fifo_data;
logic tx_fifo_rready, tx_fifo_rvalid;
logic tx_fifo_wready, tx_uart_idle;
logic tx_out;
logic tx_out_q;
logic [7:0] rx_fifo_data;
logic rx_valid, rx_fifo_wvalid, rx_fifo_rvalid;
logic rx_fifo_wready, rx_uart_idle;
logic rx_sync;
logic rx_in;
logic break_err;
logic [4:0] allzero_cnt_d, allzero_cnt_q;
logic allzero_err, not_allzero_char;
logic event_tx_watermark, event_rx_watermark, event_tx_empty, event_rx_overflow;
logic event_rx_frame_err, event_rx_break_err, event_rx_timeout, event_rx_parity_err;
logic tx_watermark_d, tx_watermark_prev_q;
logic rx_watermark_d, rx_watermark_prev_q;
logic tx_uart_idle_q;

assign tx_enable = reg2hw.ctrl.tx.q;
assign rx_enable = reg2hw.ctrl.rx.q;
assign rxnf_enable = reg2hw.ctrl.nf.q;
assign sys_loopback = reg2hw.ctrl.slpbk.q;
assign line_loopback = reg2hw.ctrl.llpbk.q;

assign uart_fifo_rxrst = reg2hw.fifo_ctrl.rxrst.q & reg2hw.fifo_ctrl.rxrst.qe;
assign uart_fifo_txrst = reg2hw.fifo_ctrl.txrst.q & reg2hw.fifo_ctrl.txrst.qe;
assign uart_fifo_rxilvl = reg2hw.fifo_ctrl.rxilvl.q;
assign uart_fifo_txilvl = reg2hw.fifo_ctrl.txilvl.q;

assign ovrd_tx_en = reg2hw.ovrd.txen.q;
assign ovrd_tx_val = reg2hw.ovrd.txval.q;

typedef enum logic {
BRK_CHK,
BRK_WAIT
} break_st_e ;

break_st_e break_st_q;

assign not_allzero_char = rx_valid & (~event_rx_frame_err | (rx_fifo_data != 8’h0));
assign allzero_err = event_rx_frame_err & (rx_fifo_data == 8’h0);

assign allzero_cnt_d = (break_st_q == BRK_WAIT || not_allzero_char) ? 5’h0 :
//allzero_cnt_q[4] never be 1b without break_st_q as BRK_WAIT
//allzero_cnt_q[4] ? allzero_cnt_q :
allzero_err ? allzero_cnt_q + 5’d1 :
allzero_cnt_q;

always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) allzero_cnt_q <= '0;
else if (rx_enable) allzero_cnt_q <= allzero_cnt_d;
end

// break_err edges in same cycle as event_rx_frame_err edges ; that way the
// reset-on-read works the same way for break and frame error interrupts.

always_comb begin
unique case (reg2hw.ctrl.rxblvl.q)
2’h0: break_err = allzero_cnt_d >= 5’d2;
2’h1: break_err = allzero_cnt_d >= 5’d4;
2’h2: break_err = allzero_cnt_d >= 5’d8;
default: break_err = allzero_cnt_d >= 5’d16;
endcase
end

always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) break_st_q <= BRK_CHK;
else begin
unique case (break_st_q)
BRK_CHK: begin
if (event_rx_break_err) break_st_q <= BRK_WAIT;
end

    BRK_WAIT: begin
      if (rx_in) break_st_q &lt;= BRK_CHK;
    end

    default: begin
      break_st_q &lt;= BRK_CHK;
    end
  endcase
end

end

assign hw2reg.val.d = rx_val_q;

assign hw2reg.rdata.d = uart_rdata;

assign hw2reg.status.rxempty.d = ~rx_fifo_rvalid;
assign hw2reg.status.rxidle.d = rx_uart_idle;
assign hw2reg.status.txidle.d = tx_uart_idle & ~tx_fifo_rvalid;
assign hw2reg.status.txempty.d = ~tx_fifo_rvalid;
assign hw2reg.status.rxfull.d = ~rx_fifo_wready;
assign hw2reg.status.txfull.d = ~tx_fifo_wready;

assign hw2reg.fifo_status.txlvl.d = tx_fifo_depth;
assign hw2reg.fifo_status.rxlvl.d = rx_fifo_depth;

// resets are self-clearing, so need to update FIFO_CTRL
assign hw2reg.fifo_ctrl.rxilvl.de = 1’b0;
assign hw2reg.fifo_ctrl.rxilvl.d = 3’h0;
assign hw2reg.fifo_ctrl.txilvl.de = 1’b0;
assign hw2reg.fifo_ctrl.txilvl.d = 2’h0;

// NCO 16x Baud Generator
// output clock rate is:
// Fin * (NCO/2**NcoWidth)
logic [NcoWidth:0] nco_sum_q; // extra bit to get the carry

always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
nco_sum_q <= 17’h0;
end else if (tx_enable || rx_enable) begin
nco_sum_q <= {1’b0,nco_sum_q[NcoWidth-1:0]} + {1’b0,reg2hw.ctrl.nco.q[NcoWidth-1:0]};
end
end

assign tick_baud_x16 = nco_sum_q[16];

//
// TX Logic //
//

assign tx_fifo_rready = tx_uart_idle & tx_fifo_rvalid & tx_enable;

prim_fifo_sync #(
.Width (8),
.Pass (1’b0),
.Depth (32)
) u_uart_txfifo (
.clk_i,
.rst_ni,
.clr_i (uart_fifo_txrst),
.wvalid_i(reg2hw.wdata.qe),
.wready_o(tx_fifo_wready),
.wdata_i (reg2hw.wdata.q),
.depth_o (tx_fifo_depth),
.full_o (),
.rvalid_o(tx_fifo_rvalid),
.rready_i(tx_fifo_rready),
.rdata_o (tx_fifo_data),
.err_o ()
);

uart_tx uart_tx (
.clk_i,
.rst_ni,
.tx_enable,
.tick_baud_x16,
.parity_enable (reg2hw.ctrl.parity_en.q),
.wr (tx_fifo_rready),
.wr_parity ((^tx_fifo_data) ^ reg2hw.ctrl.parity_odd.q),
.wr_data (tx_fifo_data),
.idle (tx_uart_idle),
.tx (tx_out)
);

assign tx = line_loopback ? rx : tx_out_q ;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
tx_out_q <= 1’b1;
end else if (ovrd_tx_en) begin
tx_out_q <= ovrd_tx_val ;
end else if (sys_loopback) begin
tx_out_q <= 1’b1;
end else begin
tx_out_q <= tx_out;
end
end

//
// RX Logic //
//

// sync the incoming data
prim_flop_2sync #(
.Width(1),
.ResetValue(1’b1)
) sync_rx (
.clk_i,
.rst_ni,
.d_i(rx),
.q_o(rx_sync)
);

// Based on: en.wikipedia.org/wiki/Repetition_code mentions the use of a majority filter
// in UART to ignore brief noise spikes
logic rx_sync_q1, rx_sync_q2, rx_in_mx, rx_in_maj;

always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
rx_sync_q1 <= 1’b1;
rx_sync_q2 <= 1’b1;
end else begin
rx_sync_q1 <= rx_sync;
rx_sync_q2 <= rx_sync_q1;
end
end

assign rx_in_maj = (rx_sync & rx_sync_q1) |
(rx_sync & rx_sync_q2) |
(rx_sync_q1 & rx_sync_q2);
assign rx_in_mx = rxnf_enable ? rx_in_maj : rx_sync;

assign rx_in = sys_loopback ? tx_out :
line_loopback ? 1’b1 :
rx_in_mx;

uart_rx uart_rx (
.clk_i,
.rst_ni,
.rx_enable,
.tick_baud_x16,
.parity_enable (reg2hw.ctrl.parity_en.q),
.parity_odd (reg2hw.ctrl.parity_odd.q),
.tick_baud (rx_tick_baud),
.rx_valid,
.rx_data (rx_fifo_data),
.idle (rx_uart_idle),
.frame_err (event_rx_frame_err),
.rx (rx_in),
.rx_parity_err (event_rx_parity_err)
);

assign rx_fifo_wvalid = rx_valid & ~event_rx_frame_err & ~event_rx_parity_err;

prim_fifo_sync #(
.Width (8),
.Pass (1’b0),
.Depth (32)
) u_uart_rxfifo (
.clk_i,
.rst_ni,
.clr_i (uart_fifo_rxrst),
.wvalid_i(rx_fifo_wvalid),
.wready_o(rx_fifo_wready),
.wdata_i (rx_fifo_data),
.depth_o (rx_fifo_depth),
.full_o (),
.rvalid_o(rx_fifo_rvalid),
.rready_i(reg2hw.rdata.re),
.rdata_o (uart_rdata),
.err_o ()
);

always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) rx_val_q <= 16’h0;
else if (tick_baud_x16) rx_val_q <= {rx_val_q[14:0], rx_in};
end


// Interrupt & Status //

always_comb begin
unique case(uart_fifo_txilvl)
2’h0: tx_watermark_d = (tx_fifo_depth < 6’d2);
2’h1: tx_watermark_d = (tx_fifo_depth < 6’d4);
2’h2: tx_watermark_d = (tx_fifo_depth < 6’d8);
default: tx_watermark_d = (tx_fifo_depth < 6’d16);
endcase
end

assign event_tx_watermark = tx_watermark_d & ~tx_watermark_prev_q;

// The empty condition handling is a bit different.
// If empty rising conditions were detected directly, then every first write of a burst
// would trigger an empty. This is due to the fact that the uart_tx fsm immediately
// withdraws the content and asserts “empty”.
// To guard against this false trigger, empty is qualified with idle to extend the window
// in which software has an opportunity to deposit new data.
// However, if software deposit speed is TOO slow, this would still be an issue.
//
// The alternative software fix is to disable tx_enable until it has a chance to
// burst in the desired amount of data.
assign event_tx_empty = ~tx_fifo_rvalid & ~tx_uart_idle_q & tx_uart_idle;

always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
tx_watermark_prev_q <= 1’b1; // by default watermark condition is true
rx_watermark_prev_q <= 1’b0; // by default watermark condition is false
tx_uart_idle_q <= 1’b1;
end else begin
tx_watermark_prev_q <= tx_watermark_d;
rx_watermark_prev_q <= rx_watermark_d;
tx_uart_idle_q <= tx_uart_idle;
end
end

always_comb begin
unique case(uart_fifo_rxilvl)
3’h0: rx_watermark_d = (rx_fifo_depth >= 6’d1);
3’h1: rx_watermark_d = (rx_fifo_depth >= 6’d4);
3’h2: rx_watermark_d = (rx_fifo_depth >= 6’d8);
3’h3: rx_watermark_d = (rx_fifo_depth >= 6’d16);
3’h4: rx_watermark_d = (rx_fifo_depth >= 6’d30);
default: rx_watermark_d = 1’b0;
endcase
end

assign event_rx_watermark = rx_watermark_d & ~rx_watermark_prev_q;

// rx timeout interrupt
assign uart_rxto_en = reg2hw.timeout_ctrl.en.q;
assign uart_rxto_val = reg2hw.timeout_ctrl.val.q;

assign rx_fifo_depth_changed = (rx_fifo_depth != rx_fifo_depth_prev_q);

assign rx_timeout_count_d =
// don’t count if timeout feature not enabled ;
// will never reach timeout val + lower power
(uart_rxto_en == 1’b0) ? 24’d0 :
// reset count if timeout interrupt is set
event_rx_timeout ? 24’d0 :
// reset count upon change in fifo level: covers both read and receiving a new byte
rx_fifo_depth_changed ? 24’d0 :
// reset count if no bytes are pending
(rx_fifo_depth == '0) ? 24’d0 :
// stop the count at timeout value (this will set the interrupt)
// Removed below line as when the timeout reaches the value,
// event occured, and timeout value reset to 0h.
//(rx_timeout_count_q == uart_rxto_val) ? rx_timeout_count_q :
// increment if at rx baud tick
rx_tick_baud ? (rx_timeout_count_q + 24’d1) :
rx_timeout_count_q;

assign event_rx_timeout = (rx_timeout_count_q == uart_rxto_val) & uart_rxto_en;

always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
rx_timeout_count_q <= 24’d0;
rx_fifo_depth_prev_q <= 6’d0;
end else begin
rx_timeout_count_q <= rx_timeout_count_d;
rx_fifo_depth_prev_q <= rx_fifo_depth;
end
end

assign event_rx_overflow = rx_fifo_wvalid & ~rx_fifo_wready;
assign event_rx_break_err = break_err & (break_st_q == BRK_CHK);

// instantiate interrupt hardware primitives

prim_intr_hw #(.Width(1)) intr_hw_tx_watermark (
.clk_i,
.rst_ni,
.event_intr_i (event_tx_watermark),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.tx_watermark.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.tx_watermark.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.tx_watermark.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.tx_watermark.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.tx_watermark.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.tx_watermark.d),
.intr_o (intr_tx_watermark_o)
);

prim_intr_hw #(.Width(1)) intr_hw_rx_watermark (
.clk_i,
.rst_ni,
.event_intr_i (event_rx_watermark),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_watermark.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.rx_watermark.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_watermark.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.rx_watermark.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.rx_watermark.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.rx_watermark.d),
.intr_o (intr_rx_watermark_o)
);

prim_intr_hw #(.Width(1)) intr_hw_tx_empty (
.clk_i,
.rst_ni,
.event_intr_i (event_tx_empty),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.tx_empty.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.tx_empty.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.tx_empty.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.tx_empty.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.tx_empty.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.tx_empty.d),
.intr_o (intr_tx_empty_o)
);

prim_intr_hw #(.Width(1)) intr_hw_rx_overflow (
.clk_i,
.rst_ni,
.event_intr_i (event_rx_overflow),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_overflow.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.rx_overflow.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_overflow.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.rx_overflow.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.rx_overflow.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.rx_overflow.d),
.intr_o (intr_rx_overflow_o)
);

prim_intr_hw #(.Width(1)) intr_hw_rx_frame_err (
.clk_i,
.rst_ni,
.event_intr_i (event_rx_frame_err),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_frame_err.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.rx_frame_err.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_frame_err.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.rx_frame_err.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.rx_frame_err.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.rx_frame_err.d),
.intr_o (intr_rx_frame_err_o)
);

prim_intr_hw #(.Width(1)) intr_hw_rx_break_err (
.clk_i,
.rst_ni,
.event_intr_i (event_rx_break_err),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_break_err.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.rx_break_err.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_break_err.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.rx_break_err.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.rx_break_err.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.rx_break_err.d),
.intr_o (intr_rx_break_err_o)
);

prim_intr_hw #(.Width(1)) intr_hw_rx_timeout (
.clk_i,
.rst_ni,
.event_intr_i (event_rx_timeout),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_timeout.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.rx_timeout.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_timeout.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.rx_timeout.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.rx_timeout.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.rx_timeout.d),
.intr_o (intr_rx_timeout_o)
);

prim_intr_hw #(.Width(1)) intr_hw_rx_parity_err (
.clk_i,
.rst_ni,
.event_intr_i (event_rx_parity_err),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_parity_err.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.rx_parity_err.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_parity_err.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.rx_parity_err.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.rx_parity_err.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.rx_parity_err.d),
.intr_o (intr_rx_parity_err_o)
);

// unused registers
logic unused_reg;
assign unused_reg = ^reg2hw.alert_test;

endmodule

core代码有点多,其核心就是一个带FIFO的uart实现。其他不分是所有模块都有的部分,是中断和错误的管理。


到现在,你会发现这个项目真是一个宝库,完全可以实践你学的设计和验证技巧,并且实现得非常规范,是一个很好的范例。

后续

罗马不是一天建成的。要在AI时代成为时代的弄潮儿,一定要打下扎实的基础,才能发挥你的创造力

后面我会介绍使用什么工具链和使用自己的工具链如何来设计和验证这个项目的每个模块。

一天一天的进步。


欢迎关注我这个新项目。

为自己加油!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/983745.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

K8S学习之基础十七:k8s的蓝绿部署

蓝绿部署概述 ​ 蓝绿部署中&#xff0c;一共有两套系统&#xff0c;一套是正在提供服务的系统&#xff0c;一套是准备发布的系统。两套系统都是功能完善、正在运行的系统&#xff0c;只是版本和对外服务情况不同。 ​ 开发新版本&#xff0c;要用新版本替换线上的旧版本&…

STM32之I2C硬件外设

注意&#xff1a;硬件I2C的引脚是固定的 SDA和SCL都是复用到外部引脚。 SDA发送时数据寄存器的数据在数据移位寄存器空闲的状态下进入数据移位寄存器&#xff0c;此时会置状态寄存器的TXE为1&#xff0c;表示发送寄存器为空&#xff0c;然后往数据控制寄存器中一位一位的移送数…

Linux基础--用户管理

目录 查看用户 使用命令: id 创建用户 使用命令: useradd ​编辑 为用户设置密码 使用命令: passwd ​编辑 删除用户 使用命令: userdel 创建用户组 使用命令: groupadd 删除用户组 使用命令: groupdel 用户设置 使用命令: usermod 将用户从组中去除 使用…

VSTO(C#)Excel开发3:Range对象 处理列宽和行高

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

本地YARN集群部署

请先完成HDFS的前置部署&#xff0c;部署方式可查看:本地部署HDFS集群https://blog.csdn.net/m0_73641796/article/details/145998092?spm1001.2014.3001.5502 部署说明 组件配置文件启动进程备注Hadoop HDFS需修改 需启动: NameNode作为主节点 DataNode作为从节点 Secondary…

每日一练之移除链表元素

题目&#xff1a; 画图解析&#xff1a; 方法&#xff1a;双指针 解答代码&#xff08;注&#xff1a;解答代码带解析&#xff09;&#xff1a; //题目给的结构体 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* }…

黑金风格人像静物户外旅拍Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 针对人像、静物以及户外旅拍照片&#xff0c;运用 Lightroom 软件进行风格化调色工作。旨在通过软件中的多种工具&#xff0c;如基本参数调整、HSL&#xff08;色相、饱和度、明亮度&#xff09;调整、曲线工具等改变照片原本的色彩、明度、对比度等属性&#xff0c;将…

【GPT入门】第8课 大语言模型的自洽性

【GPT入门】第8课 大语言模型的自洽性 1.自洽性概念2.代码&#xff08;观察执行结果&#xff09;3.自洽性核心思想 1.自洽性概念 大模型的自洽性&#xff08;self - consistency&#xff09;是指在推理阶段&#xff0c;大模型通过生成多个答案并选择出现频率最高的那个&#x…

深度学习(斋藤康毅)学习笔记(六)反向传播3

上一篇文章介绍了反向传播的自动化&#xff0c;但也存在一些问题&#xff0c;本章用于说明这些问题&#xff0c;并修改原有框架&#xff0c;使其支持复杂计算图的运行&#xff1a; 问题一&#xff1a;重复使用一个变量&#xff0c;梯度不会累计 也就是说&#xff0c;反向传播时…

3.6c语言

#define _CRT_SECURE_NO_WARNINGS #include <math.h> #include <stdio.h> int main() {int sum 0,i,j;for (j 1; j < 1000; j){sum 0;for (i 1; i < j; i){if (j % i 0){sum i;} }if (sum j){printf("%d是完数\n", j);}}return 0; }#de…

element-plus中table组件的使用

1、table组件的基本使用 注意&#xff1a; ①对象集合&#xff0c;要从后端查询。 ②prop是集合中的对象的属性名&#xff1b;label是表格表头的名称。 2、将性别一列的71转为男&#xff0c;72转为女 问题描述&#xff1a; 解决步骤&#xff1a; ①将el-table-column变成双标签…

.NET CAD 二次开发中的 Transform 与数学矩阵详解

.NET CAD 二次开发中的 Transform 与数学矩阵详解 一、Transform 的定义与作用 在 .NET CAD 二次开发中,Transform 是通过数学矩阵对图形实体进行几何变换的核心机制,包括平移、旋转、缩放、镜像和切变等操作。这些操作通过矩阵乘法实现,能够高效地修改图形的位置、方向和…

【js逆向】iwencai国内某金融网站实战

地址&#xff1a;aHR0cHM6Ly93d3cuaXdlbmNhaS5jb20vdW5pZmllZHdhcC9ob21lL2luZGV4 在搜索框中随便输入关键词 查看请求标头&#xff0c;请求头中有一个特殊的 Hexin-V,它是加密过的&#xff1b;响应数据包中全是明文。搞清楚Hexin-V的值是怎么生成的&#xff0c;这个值和cooki…

STM32之ADC

逐次逼近式ADC&#xff1a; 左边是8路输入通道&#xff0c;左下是地址锁存和译码&#xff0c;可将通道的地址锁存进ADDA&#xff0c;ADDB&#xff0c;ADDC类似38译码器的结构&#xff0c;ALE为锁存控制键&#xff0c;通道选择开关可控制选择单路或者多路通道&#xff0c;DAC为…

【Linux内核系列】:深入解析输出以及输入重定向

&#x1f525; 本文专栏&#xff1a;Linux &#x1f338;作者主页&#xff1a;努力努力再努力wz ★★★ 本文前置知识&#xff1a; 文件系统以及文件系统调用接口 用c语言简单实现一个shell外壳程序 内容回顾 那么在此前的学习中&#xff0c;我们对于Linux的文件系统已经有了…

基于YOLO11深度学习的电瓶车进电梯检测与语音提示系统【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

无人机扩频技术对比!

一、技术原理与核心差异 FHSS&#xff08;跳频扩频&#xff09; 核心原理&#xff1a;通过伪随机序列控制载波频率在多个频点上快速跳变&#xff0c;收发双方需同步跳频序列。信号在某一时刻仅占用窄带频谱&#xff0c;但整体覆盖宽频带。 技术特点&#xff1a; 抗干扰…

项目实战--网页五子棋(对战功能)(9)

上期我们完成了websocket建立连接后的数据初始化&#xff0c;今天我们完成落子交互的具体代码&#xff1a; 这里我们先复习一下&#xff0c;之前约定好的落子请求与响应包含的字段&#xff1a; 1. 发送落子请求 我们在script.js文件中找到落子的相关方法&#xff0c;增加发送请…

elementplus的cascader级联选择器在懒加载且多选时的一些问题分析

1. 背景 在之前做的一个项目中使用到了element的级联选择器&#xff0c;并且是需要懒加载、多选、父子不关联等等&#xff0c;在选的时候当然没问题&#xff0c;但是回显的时候就会回显不出来&#xff0c;相信大部分伙伴都遇到过这个问题。我在以前出过一篇文章写过关于级联选…

基于PySide6的CATIA零件自动化着色工具开发实践

引言 在汽车及航空制造领域&#xff0c;CATIA作为核心的CAD设计软件&#xff0c;其二次开发能力对提升设计效率具有重要意义。本文介绍一种基于Python的CATIA零件着色工具开发方案&#xff0c;通过PySide6实现GUI交互&#xff0c;结合COM接口操作实现零件着色自动化。该方案成…