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

news2025/3/9 6:16:59

前言

这几年芯片设计行业在国内像坐过山车。时而高亢,时而低潮。最近又因为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时代成为时代的弄潮儿,一定要打下扎实的基础,才能发挥你的创造力

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

一天一天的进步。


欢迎关注我这个新项目。

为自己加油!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

串口助手的C#编写以及有人串口服务器USR-DR301的使用

本文介绍C#编写串口程序的要点,串口服务器USR-DR301(RS232转TCP)的使用、以及调试过程中碰到的两个问题: 1). 调用串口报“连到系统上的设备没有发挥作用”. 2). “所有文本框都变成了透明”的异常处理 代码见:https://download.csdn.net/download/qq_34047402/9046713…

Android中AIDL和HIDL的区别

在Android中&#xff0c;AIDL&#xff08;Android Interface Definition Language&#xff09; 和 HIDL&#xff08;HAL Interface Definition Language&#xff09; 是两种用于定义跨进程通信接口的语言。AIDL 是 Android 系统最早支持的 IPC&#xff08;进程间通信&#xff0…

sqlserver删除表记录语句,及删除表时清零ID的SQL语句

sqlserver中&#xff0c;删除表中所有记录的语句如下 Delete from tableName 例&#xff0c;删除表logs的所有记录 sqlserver&#xff0c;删除表中所有数据&#xff0c;标识列ID归零&#xff0c;保留表结构的语句 truncate table tableName 例&#xff0c;删除表logs的所…

【瞎折腾/ragflow】构建docker镜像并部署使用ragflow

说在前面 操作系统&#xff1a;win11docker desktop版本&#xff1a;4.29.0docker engin版本&#xff1a;v26.0.0ragflow版本&#xff1a;nightly 安装docker 官网 如果是win11&#xff0c;backend建议使用wsl2 安装好后打开docker desktop&#xff0c;不然docker命令用不了 …

哈弗赛恩公式计算长度JavaScript实现

哈弗赛恩公式&#xff08;Haversine formula&#xff09;是一种用于计算球面上两点间最短距离的数学方法&#xff0c;尤其适用于地球表面。本文将详细介绍哈弗赛恩公式的原理、应用以及如何使用JavaScript实现它。 一、哈弗赛恩公式原理 在球面几何中&#xff0c;哈弗赛恩公式…

大模型赋能金融行业:从理念到落地实践

思维导图 引言 &#x1f31f; 随着人工智能技术的飞速发展&#xff0c;大模型正在重塑各行各业&#xff0c;金融领域尤为明显。本文将基于业内领先金融科技公司的实践经验&#xff0c;系统探讨大模型在金融行业的落地应用、面临的挑战以及未来的发展方向。从AI发展历程、能力边…

数据结构篇——串(String)

一、引入 在计算机中的处理的数据内容大致可分为以整形、浮点型等的数值处理和字符、字符串等的非数值处理。 今天我们主要学习的就是字符串数据。本章主要围绕“串的定义、串的类型、串的结构及其运算”来进行串介绍与学习。 二、串的定义 2.1、串的基本定义 串&#xff08;s…

数据结构--【顺序表与链表】笔记

顺序表 template <class T> class arrList :public List<T> //表示 arrList 类以公有继承的方式继承自 List<T> 类 //公有继承意味着 List<T> 类的公共成员在 arrList 类中仍然是公共成员&#xff0c;受保护成员在 arrList 类中仍然是受保护成员。 { …

算法.习题篇

算法 — 地大复试 模拟 while循环和MOD循环计数 1.约瑟夫问题 http://bailian.openjudge.cn/practice/3254 using namespace std;bool isNoPeople(vector<bool> c)//判断当前数组是否一个小孩都没有了 {bool nopeople true;for (bool ival : c){if ( ival true)nop…

大语言模型进化论:从达尔文到AI的启示与展望

文章大纲 引言大语言模型中的“进化论”思想体现遗传变异过度繁殖和生存斗争大模型“过度繁殖”与“生存竞争”机制解析**一、过度繁殖:技术迭代的指数级爆发****二、生存竞争:计算资源的达尔文战场****三、生存竞争胜出关键要素****四、行业竞争格局演化趋势**核心结论自然选…

编程考古-Borland历史:《.EXE Interview》对Anders Hejlsberg关于Delphi的采访内容(上)

为了纪念Delphi在2002年2月14日发布的25周年(2020.2.12),这里有一段由.EXE杂志编辑Will Watts于1995年对Delphi首席架构师Anders Hejlsberg进行的采访记录。在这次采访中,Anders讨论了Delphi的设计与发展,以及即将到来的针对Windows 95的32位版本。 问: Delphi是如何从T…

系统架构设计师—系统架构设计篇—基于体系结构的软件开发方法

文章目录 概述基于体系结构的开发模型-ABSDM体系结构需求体系结构设计体系结构文档化体系结构复审体系结构实现体系结构演化 概述 基于体系结构&#xff08;架构&#xff09;的软件设计&#xff08;Architecture-Based Software Design&#xff0c;ABSD&#xff09;方法。 AB…

基于Python+Django的网上招聘管理系统

项目介绍 PythonDjango网上招聘系统的设计与实现(Pycharm Django Vue Mysql) 平台采用B/S结构&#xff0c;后端采用主流的Python语言进行开发&#xff0c;前端采用主流的Vue.js进行开发。整个平台包括前台和后台两个部分。 - 前台功能包括&#xff1a;首页、岗位详情页、简历中…

Vue23Web 基礎性拉滿的面試題(2025版)還沒更新完...

Vue2&3 基礎性1. 關於Vue2和Vue3生命週期的差別2. Vue2&3組件之間傳參不同點Vue2 傳遞與接收Vue3 傳遞與接收 (使用script setup語法糖)Vue3 傳遞與接收 (不使用script setup語法糖) 3. Vue2&3 keep-alive 組件Vue2 keep-aliveVue3 keep-alive 進階性爲什麽POST請求…

GitHub神秘组织3小时极速复刻Manus

一、背景 昨夜科技圈被两个关键词刷屏&#xff1a;​Manus激活码炒至6万&#xff0c;​GitHub神秘项目OpenManus突然开源。 Manus之所以如此火爆&#xff0c;是因为在演示视频中自主分析股票、筛选简历、规划旅行的能力。同时&#xff0c;想要体验Manus就需要内测邀请码&…

文件上传漏洞(upload靶场)

目录 Pass-01&#xff1a;前端绕过 方法一&#xff1a;浏览器禁用js 方法二:直接修改或删除js脚本 方法三&#xff1a;修改后缀绕过 Pass-02:服务器检测 Pess-03:黑名单绕过 Pass-04:.htaccess文件 Pass-05:windows特性和user.ini 方法一&#xff1a;php.自动解析为ph…

苦瓜书盘官网,免费pdf/mobi电子书下载网站

苦瓜书盘&#xff08;kgbook&#xff09;是一个专注于提供6英寸PDF和MOBI格式电子书的免费下载平台&#xff0c;专为电子阅读器用户设计。该平台为用户提供了丰富的电子书资源&#xff0c;涵盖文学、历史、科学、技术等多个领域&#xff0c;旨在打造一个全面的电子书资源库。用…

Linux:理解进程,系统调用,进程,进程切换,调度,分时操作系统和实时操作系统,

操作系统要向上提供对应的服务 操作系统&#xff0c;不相信任何用户或者人&#xff1b;------------银行------窗口 因此提供了系统调用&#xff0c;&#xff08;函数调用&#xff09;-->用户和操作系统之间进行某种数据交互 一&#xff1a;系统调用 计算机的各种硬件资…

【javaEE】多线程(基础)

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…

vscode - 操作整理

文章目录 vscode - 操作整理概述笔记打开文件后&#xff0c;编码另存为配置指定后缀的文件的语言模式语言模式配置 - Batch 安装eol插件配置文件如果用vscode打开的文件没有显示回车&#xff0c;原因及处理vscode启用了信任模式&#xff0c;需要信任工作区才行。 将打开的文件中…