设计AI芯片架构的入门 研究生入行数字芯片设计、验证的项目 opentitan
前言
这几年芯片设计行业在国内像坐过山车。时而高亢,时而低潮。最近又因为AI的热潮开始high起来。到底芯片行业的规律是如何?
我谈谈自己观点:芯片设计是“劳动密集型”行业。
“EDA和工具高度标准化和代工厂的工艺标准化之后,芯片设计就变成了“劳动密集型”工作,这也是美国很长一段时间几乎要放弃芯片设计行业的技术背景。当然美国国内也没有这么多EE工程师供应。要跟制造业一样转移给全球各地。”
一家之言,大家看看笑笑就可以了。
我们国内背景是研究生大量扩招,又什么学硕/专硕、全日/非全,品种很齐。
国外研究生、美国、欧洲硕士阶段都是授课型教育,多多益善。国内现在也是。
这给研究生门很大困惑。
为什么呢?缺项目实践。
1 我一贯的观点是设计芯片和FPGA是一个技术,希望FPGA学习者也关注这方面技术。
2 AI时代入门芯片设计要先学好设计和验证,在学习架构设计
所以,本人尝试在知乎开这个栏目,提供一些稍微实战项目。一方面讲解、一方面跟各位互动。
无论是本专业科班学生还是非科班学习,也无论是你已经就业想转行,都可以参考上面的项目来学习。
网上有opentitan有介绍,让我来做第一人,来详细介绍这个项目如何学习和吃下来。
芯片设计、验证一体化项目opentitan学习
关于芯片设计、验证的入门学习书籍、以及一些小练习网上有很多书籍、视频、也有培训公司在做这方面工作。感谢他们在目前中国芯片行业急需工程师背景下所做的努力。网上做芯片验证培训的路科,我就觉得很好。我们大学这方面教学太缺了。
掌握基础知识如何通过项目提升,可能需要的时间更长,也更困难。
opentitan就是这样一个设计、验证的综合性项目。
虽然他是Google公司联合一些半导体公司以RISC-V SOC和安全芯片为样本来打造,但是完全不影响我们把它作为我们入门芯片设计和验证后的实战项目。如果要流片,你也可以采用openLane工具链来生成版图。
1 Opentitan项目介绍
才看到这个项目肯定会很懵,就让我们一点一点吃透这个项目。
除了在github开源了全部项目代码之外,我们可以通过文档学习到很多。
前几年国内成立太多芯片设计公司,建议补课。
2 芯片设计的流程(前端)
1 设计验证
https://opentitan.org/book/doc/getting_started/setup_dv.html
2 形式验证
https://opentitan.org/book/doc/getting_started/setup_formal.html
3 创建测试软件(SOC需要)
https://opentitan.org/book/doc/getting_started/build_sw.html
4 文档创建
https://opentitan.org/book/doc/getting_started/build_docs.html
5 FPGA 验证
https://opentitan.org/book/doc/getting_started/using_openocd.html
3 Opentitan芯片的设计验证
本篇文章就暂时从这款RISC-V芯片的设计和验证开始,看看我们改如何学习和下手这个项目。
简单来讲,SOC芯片就是 CPU core和 外围电路 加 总线 连接起来的功能模块。
先看看整体模块构成

可见 这是一个标准的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功能框图和代码

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: beginif (rx_in) break_st_q <= BRK_CHK;enddefault: beginbreak_st_q <= BRK_CHK;endendcase
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时代成为时代的弄潮儿,一定要打下扎实的基础,才能发挥你的创造力
后面我会介绍使用什么工具链和使用自己的工具链如何来设计和验证这个项目的每个模块。
一天一天的进步。
欢迎关注我这个新项目。
为自己加油!
相关文章:
设计AI芯片架构的入门 研究生入行数字芯片设计、验证的项目 opentitan
前言 这几年芯片设计行业在国内像坐过山车。时而高亢,时而低潮。最近又因为AI的热潮开始high起来。到底芯片行业的规律是如何? 我谈谈自己观点:芯片设计是“劳动密集型”行业。 “EDA和工具高度标准化和代工厂的工艺标准化之后,芯…...
【弹性计算】异构计算云服务和 AI 加速器(二):适用场景
异构计算云服务和 AI 加速器(二):适用场景 1.图形处理2.视频处理3.计算4.人工智能 异构计算 目前已经被广泛地应用于生产和生活当中,主要应用场景如下图所示。 1.图形处理 GPU 云服务器在传统的图形处理领域具有强大的优势&…...
JVM常用概念之移动GC和局部性
问题 非移动GC一定比移动GC好吗? 基础知识 移动GC和非移动GC 移动GC 在进行垃圾回收时,为了减少碎片而移动对象来顺利完成垃圾回收的GC。 Serial GC 在单线程环境下,它在标记-清除(Mark-Sweep)算法的基础上进行…...
微服务保护:Sentinel
home | Sentinelhttps://sentinelguard.io/zh-cn/ 微服务保护的方案有很多,比如: 请求限流 线程隔离 服务熔断 服务故障最重要原因,就是并发太高!解决了这个问题,就能避免大部分故障。当然,接口的并发…...
使用Wireshark截取并解密摄像头画面
在物联网(IoT)设备普及的今天,安全摄像头等智能设备在追求便捷的同时,往往忽视了数据传输过程中的加密保护。很多摄像头默认通过 HTTP 协议传输数据,而非加密的 HTTPS,从而给潜在攻击者留下了可乘之机。本文…...
IDEA 基础配置: maven配置 | 服务窗口配置
文章目录 IDEA版本与MAVEN版本对应关系maven配置镜像源插件idea打开服务工具窗口IDEA中的一些常见问题及其解决方案IDEA版本与MAVEN版本对应关系 查找发布时间在IDEA版本之前的dea2021可以使用maven3.8以及以前的版本 比如我是idea2021.2.2 ,需要将 maven 退到 apache-maven-3.…...
20250-3-8 树的存储结构
一、树的逻辑结构回顾 树:一个分支结点可以有多课子树 如果按照二叉树的存储来实现树的存储,则只依靠数组下标,无法反映结点之间的逻辑关系。 二、双亲表示法(顺序存储) 1.因此:我们可以用链式存储的方法&…...
Visual-RFT视觉强化微调:用「试错学习」教会AI看图说话
📜 文献卡 英文题目: Visual-RFT: Visual Reinforcement Fine-Tuning;作者: Ziyu Liu; Zeyi Sun; Yuhang Zang; Xiaoyi Dong; Yuhang Cao; Haodong Duan; Dahua Lin; Jiaqi WangDOI: 10.48550/arXiv.2503.01785摘要翻译: 像OpenAI o1这样的大型推理模型中的强化微调…...
PDF处理控件Aspose.PDF,如何实现企业级PDF处理
PDF处理为何成为开发者的“隐形雷区”? “手动调整200页PDF目录耗时3天,扫描件文字识别错误导致数据混乱,跨平台渲染格式崩坏引发客户投诉……” 作为开发者,你是否也在为PDF处理的复杂细节消耗大量精力?Aspose.PDF凭…...
DeepSeek-R1本地化部署(Mac)
一、下载 Ollama 本地化部署需要用到 Ollama,它能支持很多大模型。官方网站:https://ollama.com/ 点击 Download 即可,支持macOS,Linux 和 Windows;我下载的是 mac 版本,要求macOS 11 Big Sur or later,Ol…...
Swift Package Manager (SPM) 创建并集成本地库
在macOS 项目中,使用 Swift Package Manager (SPM) 创建并集成本地库的完整步骤。 创建一个macos应用程序,选择 swift、oc、swiftui都可以。 创建好应用之后,开始创建SPM本地库。 打开终端app,进入项目根目录,逐次输…...
分布式锁—6.Redisson的同步器组件
大纲 1.Redisson的分布式锁简单总结 2.Redisson的Semaphore简介 3.Redisson的Semaphore源码剖析 4.Redisson的CountDownLatch简介 5.Redisson的CountDownLatch源码剖析 1.Redisson的分布式锁简单总结 (1)可重入锁RedissonLock (2)公平锁RedissonFairLock (3)联锁MultiL…...
文献分享: ConstBERT固定数目向量编码文档
😂图放这了,大道至简的 idea \text{idea} idea不愧是 ECIR \text{ECIR} ECIR 👉原论文 1. ConstBERT \textbf{1. ConstBERT} 1. ConstBERT的原理 1️⃣模型的改进点:相较于 ColBERT \text{ColBERT} ColBERT为每个 Token \text{Tok…...
如何使用SSH命令安全连接并转发端口到远程服务器
ssh -p 22546 rootconnect.westc.gpuhub.com d6IS/mQKq/iG ssh -CNgv -L 6006:127.0.0.1:6006 rootconnect.westc.gpuhub.com -p 22546 第一条命令:用于登录远程服务器,进行交互式操作。第二条命令:用于建立 SSH 隧道,进行端口转…...
SolidWorks 转 PDF3D 技术详解
在现代工程设计与制造流程中,不同软件间的数据交互与格式转换至关重要。将 SolidWorks 模型转换为 PDF3D 格式,能有效解决模型展示、数据共享以及跨平台协作等问题。本文将深入探讨 SolidWorks 转 PDF3D 的技术原理、操作流程及相关注意事项,…...
9.2 EvictionManager源码解读
本节重点总结 : evictionManager初始化了两个相同的manager对象 evictionManager做本机驱逐pod的判定和厨房evictionAdmitHandler用来kubelet创建Pod前进依据本机的资源压力进行准入检查 evictionManager判断内存驱逐阈值有两种方法 第一种使用内核的memcg的通知机制ÿ…...
考研数一非数竞赛复习之Stolz定理求解数列极限
在非数类大学生数学竞赛中,Stolz定理作为一种强大的工具,经常被用来解决和式数列极限的问题,也被誉为离散版的’洛必达’方法,它提供了一种简洁而有效的方法,使得原本复杂繁琐的极限计算过程变得直观明了。本文&#x…...
整理一下高级设施农业栽培学这门课程的所有知识点
整理一下高级设施农业栽培学这门课程的所有知识点 以下是高级设施农业栽培学这门课程从入门到精通需要学习的知识点: 一、设施农业概述 设施农业的概念与发展历程 了解设施农业的定义、特点及作用,掌握其发展历程、现状与未来趋势。熟悉国内外设施农业…...
2025最新软件测试面试八股文(含答案+文档)
1、请试着比较一下黑盒测试、白盒测试、单元测试、集成测试、系统测试、验收测试的区别与联系。 参考答案: 黑盒测试:已知产品的功能设计规格,可以进行测试证明每个实现了的功能是否符合要求。 白盒测试:已知产品的内部工作过程…...
系统架构设计师—系统架构设计篇—基于体系结构的软件开发方法
文章目录 概述基于体系结构的开发模型-ABSDM体系结构需求体系结构设计体系结构文档化体系结构复审体系结构实现体系结构演化 概述 基于体系结构(架构)的软件设计(Architecture-Based Software Design,ABSD)方法。 AB…...
求最大公约数【C/C++】
大家好啊,欢迎来到本博客( •̀ ω •́ )✧,我将带领大家详细的了解最大公约数的思想与解法。 一、什么是公约数 公约数,也称为公因数,是指两个或多个整数共有的因数。具体来说,如果一个整数能被两个或多个整数整除&…...
Transformer 代码剖析16 - BLEU分数(pytorch实现)
一、BLEU算法全景图 #mermaid-svg-uwjb5mQ2KAC6Rqbp {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-uwjb5mQ2KAC6Rqbp .error-icon{fill:#552222;}#mermaid-svg-uwjb5mQ2KAC6Rqbp .error-text{fill:#552222;stroke:…...
手机屏幕摔不显示了,如何用其他屏幕临时显示,用来导出资料或者清理手机
首先准备一个拓展坞 然后 插入一个外接的U盘 插入鼠标 插入有数字小键盘区的键盘 然后准备一根高清线,一端链接电脑显示器,一端插入拓展坞 把拓展坞的连接线,插入手机充电口(可能会需要转接头) 然后确保手机开机 按下键盘…...
labelimg标注的xml标签转换为yolo格式标签
本文不生产技术,只做技术的搬运工!!! 前言 在yolo训练时,我们需要对图像进行标注,而使用labelimg标注时如果直接选择输出yolo格式的数据集,则原始数据的很多信息无法被保存,因此一版…...
Linux云计算SRE-第十七周
1. 做三个节点的redis集群。 1、编辑redis节点node0(10.0.0.100)、node1(10.0.0.110)、node2(10.0.0.120)的安装脚本 [rootnode0 ~]# vim install_redis.sh#!/bin/bash # 指定脚本解释器为bashREDIS_VERSIONredis-7.2.7 # 定义Redis的版本号PASSWORD123456 # 设置Redis的访问…...
K8S学习之基础十八:k8s的灰度发布和金丝雀部署
灰度发布 逐步扩大新版本的发布范围,从少量用户逐步扩展到全体用户。 特点是分阶段发布、持续监控、逐步扩展 适合需要逐步验证和降低风险的更新 金丝雀部署 将新版本先部署到一小部分用户或服务器,观察其表现,再决定是否全面推广。 特点&…...
WSL with NVIDIA Container Toolkit
一、wsl 下安装 docker 会提示安装 docekr 桌面版,所以直接安装 docker 桌面版本即可 二、安装 NVIDIA Container Toolkit NVIDIA Container Toolkit仓库 https://github.com/NVIDIA/nvidia-container-toolkitgithub.com/NVIDIA/nvidia-container-toolkit 安装…...
PAT线上考试 真题/注意细节(甲/乙级)
闲谈 从此以后!参加竞赛! 都要为自己留够足够的时间练习! 都要为自己留够足够的时间练习! 都要为自己留够足够的时间练习! 重要的事情说三遍,毕竟这只是我参加各种竞赛的开始! \(ÿ…...
springcloud sentinel教程
QPS(Queries Per Second)即每秒查询率 TPS,每秒处理的事务数目 PV(page view)即页面浏览量 UV 访问数(Unique Visitor)指独立访客访问数 一、初识Sentinel 什么是雪崩问题? 微服务之间相…...
摄相机标定的基本原理
【相机标定的基本原理与经验分享】https://www.bilibili.com/video/BV1eE411c7kr?vd_source7c2b5de7032bf3907543a7675013ce3a 相机模型: 定义: 内参:就像相机的“眼睛”。它描述了相机内部的特性,比如焦距(镜头的放…...
HJ C++11 Day2
Initializer Lists 对于一个类P class P{P(int a, int b){cout << "P(int, int), a" << a << ", b " << b << endl;}P(initializer_list<int> initlist){cout << "P(initializer_list<int>), val…...
在 ASP.NET Core 中启用 Brotli 和 Gzip 响应压缩
在本文中,我们将探讨如何在 ASP.NET Core 应用程序中启用响应压缩,重点介绍 Brotli 和 Gzip 算法以及如何验证压缩是否有效。 什么是响应压缩? 响应压缩通过使用Brotli 或 Gzip等算法来最小化 HTTP 响应的大小。这些算法在传输文本资产&#…...
leetcode69.x 的平方根
题目: 给你一个非负整数 x ,计算并返回 x 的 算术平方根 。 由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。 注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。…...
第11章 web应用程序安全(网络安全防御实战--蓝军武器库)
网络安全防御实战--蓝军武器库是2020年出版的,已经过去3年时间了,最近利用闲暇时间,抓紧吸收,总的来说,第11章开始学习利用web应用程序安全,主要讲信息收集、dns以及burpsuite,现在的资产测绘也…...
flac、kgg、kgma格式音频转换MP3
1. 选择需要转换的音频文件 2. 下载闪电音频格式转换器 闪电音频格式转换器-全面覆盖常见音乐格式_音频合并分割_音频压缩 3. 买会员有点贵,也没必要,偶尔转换一次的,就去闲鱼买,两天会员9块钱。 4. 闲鱼卖家给兑换码,…...
macos 程序 运行
sudo xattr -r -d com.apple.quarantine [/Applications/Name]使用stow 管理配置文件...
基于YOLO11深度学习的电瓶车进电梯检测与语音提示系统【python源码+Pyqt5界面+数据集+训练代码】
《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...
HTML5 表单属性
HTML5 表单属性 引言 HTML5作为新一代的网页标准,带来了许多新的特性和改进。在表单处理方面,HTML5引入了一系列新的表单属性,这些属性使得表单的创建和使用更加灵活和强大。本文将详细介绍HTML5表单属性,包括其功能、使用方法和注意事项。 一、HTML5表单属性概述 HTML…...
从0开始,手搓Tomcat
一、什么是Tomcat Tomcat 是一款开源的、轻量级的 Web 服务器,它不仅能够提供 HTTP 服务,还能够运行 Java Servlet 和 JavaServer Pages(JSP)。对于许多开发者来说,理解 Tomcat 的目录结构以及如何在该结构中组织应用…...
数列分块入门2
题目描述 给出一个长为 n n n 的数列,以及 n n n 个操作,操作涉及区间加法,询问区间内小于某个值 x x x 的元素个数。 输入格式 第一行输入一个数字 n n n。 第二行输入 n n n 个数字,第 i i i 个数字为 a i a_i ai&a…...
【ThreeJS Basics 06】Camera
文章目录 Camera 相机PerspectiveCamera 透视相机正交相机用鼠标控制相机大幅度转动(可以看到后面) 控制组件FlyControls 飞行组件控制FirstPersonControls 第一人称控制PointerLockControls 指针锁定控制OrbitControls 轨道控制TrackballControls 轨迹球…...
postman接口请求中的 Raw是什么
前言 在现代的网络开发中,API 的使用已经成为数据交换的核心方式之一。然而,在与 API 打交道时,关于如何发送请求体(body)内容类型的问题常常困扰着开发者们,尤其是“raw”和“json”这两个术语之间的区别…...
docker1
前言 技术架构 单机架构 应用数据分离架构 应用服务集群架构 读写分离/主从分离架构 写入主的时候,要同步Mysql从的数据才可以 冷热分离架构 写的时候要写入主和缓存数据库 读的时候先去缓存看有没有,没有的话就去从数据库读数据 主要就是看这个数据是…...
RocketMQ延迟消息深度解析:原理、实践与性能调优
RocketMQ延迟消息深度解析:原理、实践与性能调优 编程相关书籍分享:https://blog.csdn.net/weixin_47763579/article/details/145855793 DeepSeek使用技巧pdf资料分享:https://blog.csdn.net/weixin_47763579/article/details/145884039 一、…...
RabbitMQ 高级特性解析:RabbitMQ 消息可靠性保障 (上)
RabbitMQ 核心功能 RabbitMQ 高级特性解析:RabbitMQ 消息可靠性保障 (上)-CSDN博客 RabbitMQ 高级特性:从 TTL 到消息分发的全面解析 (下)-CSDN博客 前言 最近再看 RabbitMQ,看了看自己之前写…...
大白话JavaScript实现一个函数,将数组中的元素进行去重
大白话JavaScript实现一个函数,将数组中的元素进行去重 答题思路 要实现数组元素去重的函数,核心思路是遍历数组,然后判断每个元素是否已经在新数组里存在,如果不存在就添加进去,存在则跳过。下面会介绍几种不同的实…...
PQL查询和监控各类中间件
1 prometheus的PQL查询 1.1 Metrics数据介绍 prometheus监控中采集过来的数据统一称为Metrics数据,其并不是代表具体的数据格式,而是一种统计度量计算单位当需要为某个系统或者某个服务做监控时,就需要使用到 metrics prometheus支持的met…...
uni_app实现下拉刷新
1. 在页面配置中启用下拉刷新 首先,你需要在页面的 pages.json 文件中启用下拉刷新功能。 {"pages": [{"path": "pages/index/index","style": {"navigationBarTitleText": "首页","enablePull…...
C#类型转换基本概念
一、基本定义 C# 类型转换是将数据从一种类型转换为另一种类型的过程,分为 隐式转换 和 显式转换 两类。 强类型语言特性:C# 要求变量类型在编译时确定,类型转换需满足兼容性或显式规则。目的:处理不同数据类…...
【学习笔记】【DeepSeek AI 医生】2-2 AI家庭医生课程内容介绍
【DeepSeek AI 医生】2-4 项目详细分析及DeepSeek适用场景 一、Ollama部署二、可视化UI三、构建项目环境四、搭建项目架构五、Spring Al六、SSE服务端推送事件七、数据持久化八、线上部署 一、Ollama部署 Mac部署windows 部署ollama脚本、常用命令DeepSeek 提示词、角色、适用…...