ASIC-WORLD Verilog(1)一日Verilog

news2024/11/27 16:51:46

写在前面

        在自己准备写一些简单的verilog教程之前,参考了许多资料----asic-world网站的这套verilog教程即是其一。这套教程写得极好,奈何没有中文,在下只好斗胆翻译过来(加了自己的理解)分享给大家。

        这是网站原文:Verilog Tutorial

        这是系列导航:Verilog教程系列文章导航

介绍

        Verilog 是一种硬件描述语言 (HARDWARE DESCRIPTION LANGUAGE ,HDL)。硬件描述语言是一种用于描述数字系统(例如网络交换机、微处理器或存储器或简单的触发器)的语言。这意味着你可以通过使用 HDL来描述任何级别的任何(数字)硬件。

//D触发器
module d_ff ( d, clk, q, q_bar);

input d ,clk;
output q, q_bar;
wire d ,clk;
reg q, q_bar;
       
always @(posedge clk) begin
  q <= d;
  q_bar <= ! d;
end

endmodule

        通过硬件描述语言,你可以描述一个如上图所示的简单D触发器,也可以描述一个超过100 万个逻辑门的复杂设计。Verilog 是业界可用于硬件设计的 HDL 语言之一。它允许我们在行为级(Behavior Level)、寄存器传输级 (Register Transfer Level,RTL)、门级(Gate level)和开关级(switch level)进行数字设计。Verilog 允许硬件设计人员使用行为结构来表达他们的设计,将实现细节推迟到最终设计的后期阶段。

        很多想学这门语言的工程师,经常会问这样一个问题:"学Verilog需要多少时间?" 对此我的答案是“如果你碰巧知道至少一种编程语言的话(比如C、Java等),那可能只需要一周的时间。”

设计风格

        Verilog 与任何其他硬件描述语言一样,允许采用自下而上(Bottom-up)或自上而下(Top-down)的方法进行设计。

自下而上的设计

        传统的电子设计方法是自下而上的,每个设计都是使用标准门电路在门级实现的。随着新设计变得越来越复杂,这种方法几乎无法维护。新系统由 ASIC 或微处理器组成,复杂性达到了数千个晶体管级别。这种传统的自下而上的方法必须让位于新的结构化、层次化的设计方法。没有这些新方法,就无法解决新设计的复杂性。

自上而下的设计

        所有设计师都期望的设计风格是自上而下的。真正的自上向下设计允许早期测试、不同技术的轻松更改、结构化系统设计并能提供许多其他优势。但是要实现纯粹的自上而下的设计是非常困难的,基于这个事实,大多数设计都是这两种方法(自下而上和自上而下)的混合,以结合两种方法的优点。

        下图展示了自上而下的设计方法。

Verilog的抽象层级

        Verilog 支持在不同的抽象层级上进行设计。其中三个非常重要:

  • 行为级(Behavioral level)
  • 寄存器转换级(Register-Transfer Level)
  • 门级(Gate Level)

行为级

        通过并行算法(行为)来描述系统。每个算法本身都是有顺序的,这意味着它由一组依次执行的指令组成。函数(Functions)、任务(Tasks)和 Always 块(Always blocks)是主要元素。行为级不考虑设计的结构实现。

寄存器转换级(RTL)

        使用寄存器传输级别的设计通过操作和寄存器之间的数据传输来指定电路的特性。这需要使用显式的时钟信号。RTL 设计包含精确的时序边界:操作被安排在特定的时间发生。现代 RTL 代码定义是“任何可综合的代码都称为 RTL 代码”。

门级

        在逻辑层内,系统的特性由逻辑链路及其时序属性描述。所有的信号都是离散信号,它们只能是明确的逻辑值('0'、'1'、'X'、'Z')。可用的操作是预定义的逻辑原语(AND、OR、NOT 等门)。对于任何级别的逻辑设计来说,使用门级建模可能都不是一个好主意。门级代码由综合工具等工具生成,此网表一般用于门级仿真和后端。

再介绍

        每一个初学者的梦想都是能在一天之内掌握Verilog,至少也要能使用它。为了帮助初学者实现这个梦想,我接下来将讲解一些理论、示例和练习。

        本教程不会教您如何编程(软件编程),因为它是为那些有一定编程(软件编程)经验的人设计的。Verilog 并发执行不同的代码块----尽管这与大多数软件编程语言的顺序执行所相反,但二者之间仍有许多相似之处。

        此外,一些数字电路的设计背景也对学习Verilog很有帮助。

        Verilog 出现之前的设计主要依赖原理图。每个设计,无论复杂程度如何,都是通过原理图设计的。它们难以验证且容易出错,这就很容易形成设计、验证、设计、验证……的漫长且乏味的开发迭代过程。

        当 Verilog 出现后,我们突然有了一种新的思考方式来认识逻辑电路。Verilog 设计周期更像是一个传统的编程周期,本教程将引导您完成它。

        使用Verilog进行设计的流程是这样的:

  • 规格 (Specifications,specs)
  • 高层级设计(High level design)
  • 低层级设计(Low level (micro) design)
  • RTL编码(RTL coding)
  • 验证(Verification)
  • 综合(Synthesis)

        列表中的第一个是规格----我们将对设计施加何种限制和要求?我们要构建什么?

        在本教程中,我们将构建一个2路仲裁器(2 agent arbiter):一种在2个源之间进行选择以争夺控制权(mastership)的设备。以下是我们可能会编写的一些规格。

  • 2路仲裁器
  • 高电平有效的异步复位
  • 源 0 优先于源1的固定优先级
  • 只要请求被断言,授权就会被断言(意思就是只要输入有效,输出就有效)

        一旦我们有了规格,就可以绘制框图了,它基本上算是系统内数据流的一种抽象(什么进入黑盒子?或什么从黑盒子出来?)。我们举的例子很简单,对应的框图如下所示。现在我们还不用担心神奇的黑盒子里有什么。

仲裁器框图

        如果现在我们不用Verilog来设计这个仲裁器,标准程序将要求我们绘制一个状态机,然后我们将为每个触发器制作一个包含状态转换的真值表,跟着我们会绘制卡诺图,根据卡诺图我们可以得到优化后的电路。此方法适用于小型设计,但对于大型设计,此流程将变得复杂且容易出错。这就是 Verilog 的用武之地,它向我们展示了另一种方式。

低层级设计

        要了解 Verilog 是如何帮助我们设计仲裁器的话,让我们继续聚焦在状态机----现在我们进入低级设计并剥开上一个框图中黑盒子的封面,来看看输入是如何影响仲裁器的。

        圆圈代表仲裁器可能处于的状态,每个状态都对应一个输出。状态之间的箭头是状态转换,由导致转换的事件标记。例如,最左边的橙色箭头表示如果仲裁器处于状态 GNT0(输出对应于 GNT0 的信号)并接收到输入 !req_0时,就会移动到状态 IDLE 并输出对应于该状态的信号。这个状态机描述了你所需要的系统的所有逻辑状态。下一步是将其全部放入 Verilog 中。

模块(Modules)

        我们需要回溯一下才能做到这一点。如果你观察第一张图片中的仲裁器,就会发现它有一个名称(“仲裁器”)和输入/输出端口(req_0、req_1、gnt_0 和 gnt_1)。

        由于Verilog是一种HDL(硬件描述语言,一种用于集成电路概念设计的语言),它也需要具备这些东西(名称和输入/输出端口)。在 Verilog 中,我们把“黑盒子”称为模块(Modules)。这是Verilog中的保留字,用于指代具有输入、输出和内部逻辑的事物。它们是其他编程语言中具有返回值的函数(Functions)的类似概念。

“仲裁器”模块的代码

        如果你仔细观察仲裁块,就会看到有许多的箭头标记(进入模块的是输入,从模块出去的则是输出)。在Verilog中,我们声明了模块名和端口名之后,就可以定义每个端口的方向了。其RTL代码如下所示。

module arbiter (
clock      , // clock
reset      , // Active high, syn reset
req_0      , // Request 0
req_1      , // Request 1
gnt_0      , // Grant 0
gnt_1        // Grant 1
);

//-------------Input Ports-----------------------------
input           clock               ;
input           reset               ;
input           req_0               ;
input           req_1               ;

//-------------Output Ports----------------------------
output        gnt_0                 ;
output        gnt_1                 ;

双向端口示例

        上面我们只使用了两种类型的端口,输入input和输出output,此外,还有一种双向端口也可以使用----inout。

inout read_enable;     // 名为 read_enable 的端口是双向的

向量(vector )信号示例

        应该如何定义向量信号(由多于一位的信号组成的序列)呢?Verilog 提供了一种简单的方法。

inout [7:0] address;    //端口“address”是双向的

        请注意 [7:0] 意味着我们使用的是little-endian(低字节序)约定----从最右边的 0 位开始依次向左递增。

        如果我们使用的是 [0:7],意味着我们使用的是big-endian(高字节序)约定----从最左边的 0 位开始依次向右递增。

        Endianness (字节序)是一种决定数据“读取”方式的排序方式,不同的系统之间确实存在差异,因此始终使用正确的 endianness 很重要。作为类比,想想一些从左到右(高字节序)书写的语言(英语)与从右到左(低字节序)书写的其他语言(阿拉伯语)。了解语言的书写方向对于能够阅读它至关重要,但方向本身是在几年前任意设置的。

概括

  • 我们了解了如何在 Verilog 中定义块/模块(block/module)
  • 我们学习了如何定义端口和端口方向(ports and port directions)
  • 我们学习了如何声明向量/标量端口(vector/scalar)

数据类型

        数据类型与硬件有什么关系?实际上,没什么关系。人们只是想再写一种包含数据类型的语言。但是等等……硬件确实有两种驱动(Drivers)。驱动?那是什么?

        驱动是一种可以驱动负载的数据类型。在物理电路中,驱动器基本上可以是电子可以穿过/进入的任何东西。

  • 可以存储数值的驱动(例如:触发器(flip-flop))
  • 不能存储值,但能连接两点的驱动(例如:线(wire))。

        第一种类型的驱动在 Verilog 中称为 reg(“register”的缩写)。第二种数据类型称为线(wire)。此外还有很多其他的数据类型,例如,寄存器可以是有符号的、无符号的、浮点数……作为新手,你现在暂时不要管这些。

示例:

wire and_gate_output; // "and_gate_output" 是只输出的线
reg d_flip_flop_output;// "d_flip_flop_output" 是一个寄存器;它存储并输出数值
reg [7:0] address_bus;// "address_bus" 是一个低字节序的 8 位寄存器

概括

  • Wire 数据类型用于连接两点
  • Reg 数据类型用于存储数值

运算符

        值得庆幸的是,verilog中的运算符与其他编程语言中的相同。他们取两个值并比较(或其他的运算方式)它们以产生第三个结果----常见的例子是加法、等于、逻辑与……为了让我们的生活更轻松,几乎所有的运算符(至少下面列表中的那些)与它们在 C 语言中的对应部分完全相同。

类型

符号

功能

算术运算符

*

乘法

/

除法

+

加法

-

减法

%

取余

+

一元加法(Unary plus)

-

一元减法(Unary minus)

逻辑运算符

!

逻辑非

&&

逻辑与

||

逻辑或

关系运算符

>

大于

<

小于

>=

大于等于

<=

小于等于

相等运算符

==

等于

!=

不等于

归约操作符

~

按位取反

~&

与非

|

~|

活飞

^

异或

^~

同或

~^

同或

移位运算符

>>

向右移位

<<

向左移位

拼接运算符

{ }

拼接

条件运算符

?

条件

示例

a = b + c ;     // 这很容易
a = 1 << 5;     // 嗯,让我想想,好吧,将 '1' 左移 5 个位置。
a = !b ;        // 它会反转 b 吗???
a = ~b ;        // 你还想给'a'赋值多少次?这可能会导致多驱动(multiple-drivers)问题。


控制语句

        等等,这是什么?if, else, repeat, while, for, case----Verilog 看起来和 C 语言一模一样(或者其他你会使用的编程语言)!尽管功能上verilog看起来与 C语言相同,但 Verilog 是一种 HDL,因此描述应被转化为硬件。这意味着您在使用控制语句时必须小心(否则您的设计可能无法在硬件中实现)。

If-else语句

If-else 语句通过检查条件来决定执行哪部分代码。如果满足条件,则执行该条件对应的代码。否则,它会运行代码的其他部分。

if (enable == 1'b1) begin
  data = 10;             //10进制赋值
  address = 16'hDEAD;     //16进制赋值
  wr_enable = 1'b1;     // 2进制赋值
end 
else begin
  data = 32'b0;
  wr_enable = 1'b0;
  address = address + 1;  
end

        你可以在条件检查中使用任何运算符,就像 C 语言的用法一样。如果需要,我们也可以嵌套 if-else 语句。没有 else 的语句也是合法的,但是它们可能会有其他问题----在实现组合逻辑时容易产生锁存器Latch。

Case语句

        当我们有一个变量需要检查多个值时,可以使用 Case 语句。就像地址解码器一样,输入是一个地址,且需要检查它的所有可能值。我们没有使用多个嵌套的 if-else 语句(一个对应于我们要查找的单个值),而是使用单个 case 语句----这类似于 C/C++ 等语言中的 Switch 语句。

        Case 语句以case开头,以endcase结束。在这两个分隔符之间列出你希望执行的所有语句(后跟冒号:)。

        写default语句是一个好主意,就像有限状态机 (FSM) 一样,如果进入了非定义状态,状态机就被挂起(“死机”)。带有返回功能的default语句可以保证我们的安全。

case(address)
  0 : $display ("It is 11:40PM");
  1 : $display ("I am feeling sleepy");
  2 : $display ("Let me skip this tutorial");
  default : $display  ("Need to complete");
endcase

注意: if-else 和 case 语句有一个共同点----如果您没有涵盖到所有可能的情况(If-else 中没有“else”或 Case 中没有“default”),并且你正在写一个组合逻辑语句的话,那么综合工具就会推断出锁存器Latch。

While语句

        如果它检查的条件为真,那么 while 语句就会重复执行其中的代码。While循环通常不用于现实电路中的模型,一般都用于测试脚本testbench。与其他语句块一样,它们由begin和end分隔。

while (free_time) begin
  $display ("Continue with webpage development");
end

        只要 free_time 变量为真,就会重复执行 begin 和 end 内的代码----即打印“Continue with webpage development”。

        让我们来看一个更奇怪的例子,它使用了大部分的 Verilog 结构。

module counter (clk,rst,enable,count);
input clk, rst, enable;
output [3:0] count;
reg [3:0] count;
                      
always @ (posedge clk or posedge rst)
    if (rst) begin
      count <= 0;
    end 
    else begin : COUNT
      while (enable) begin
        count <= count + 1;
        disable COUNT;
      end
end

endmodule

        您会注意到一个名为 always 的新块----这说明了 Verilog 的一个关键特性。正如我们之前提到的,大多数软件语言都是按顺序执行的。相反,Verilog 程序通常有许多并行执行的语句。当满足其中列出的一个或多个条件时,所有标记为always 的块都将同时运行。

        在上面的示例中,always 块将在 rst 或 clk 达到上升沿时运行----当它们的值从 0 上升到 1 时。您可以在程序中同时运行两个或多个always块(此处未显示,但常用)。

        我们可以通过无效化保留字的方式来无效化代码块。在上面的示例中,每次计数器递增后,COUNT 代码块(此处未显示)都会被无效化。

For语句

        Verilog 中的 for 循环几乎与 C/C++ 中的 for 循环完全相同。唯一的区别是 Verilog 不支持 ++ 和 -- 运算符,所以和在 C 中那样编写 i++ 不同,你需要写出完整的 i = i + 1。

for (i = 0; i < 16; i = i +1) begin
      $display ("Current value of i is %d", i);
end

        此代码将按顺序打印从 0 到 15 的数字。将 for 循环用于RTL时要小心,需要确保你的代码实际上可以在硬件中正常实现……并且循环不是无限的。

Repeat语句

        repeat 语句类似于我们刚刚介绍的 for 循环。与我们声明 for 循环时显式地指定一个变量并递增它不同,repeat语句是告诉程序应该运行代码多少次,且没有变量递增(除非我们希望它们如此,就像在这个例子中)。

repeat (16) begin
    $display ("Current value of i is %d", i);
    i = i + 1;
end

        输出与前面的 for 循环程序示例完全相同。在实际硬件实现中我们一般很少使用repeat语句(或 for 循环)。

概括

  • while、if-else、case(switch)语句和C语言中的一样
  • If-else 和 case 语句要求涵盖组合逻辑的所有情况
  • For 循环与 C 中的相同,但没有 ++ 和 -- 运算符
  • Repeat 与 for 循环相同,但没有递增变量

变量赋值

        在数字电路中有两种类型的电路,组合逻辑电路和时序逻辑电路。我们当然知道这一点,但问题是“我们应该如何在 Verilog 中对此建模?”。Verilog 提供了两种对组合逻辑进行建模的方法和一种对时序逻辑进行建模的方法。

  • 可以使用 assign 和 always 语句对组合逻辑电路建模
  • 只能使用 always 语句对时序逻辑电路建模
  • 还有第三个块,仅在testbench中(实际上也可以在RTL中进行初始化,但不常见)使用:称为Initial语句。

Initial块

        顾名思义,Initial块(出师快)仅在仿真开始时执行一次。这在编写testbench时很有用。如果我们有多个Initial块,那么它们都会在仿真开始时执行。

示例

initial begin
    clk = 0;
    reset = 0;
    req_0 = 0;
    req_1 = 0;
end

        在上面的示例中,在仿真开始时(即当仿真时间为0 时),begin-end内的所有变量都会被赋值为零。


Always块

        顾名思义,always 块总是在执行,这与仅执行一次(在仿真开始时)的Initial块不同。第二个区别是 always 块应该有一个敏感列表或与之关联的延迟。

        敏感列表告诉 always 块何时执行代码块,如下图所示。保留字alway后的@符号表示代码块将在符号@后括号中的条件“触发”。

        关于 always 块的一个重要说明:它不能驱动 wire 数据类型,但可以驱动 reg 和 integer 数据类型。

always  @ (a or b or sel)begin
  y = 0;
  if (sel == 0) begin
    y = a;
  end 
  else begin
    y = b;
  end
end

        上面的例子是一个 2选1的多路选择器, a 和 b是输入,sel 是选择,y 是输出。

        在任何组合逻辑电路中,只要输入发生变化,输出就会立即发生变化。当应用于 always 块时,该理论意味着只要输入变量(或输出控制变量)发生变化,就需要执行 always 块中的代码。这些变量是包含在敏感列表中的变量,即 a、b 和 sel。

        有两种类型的敏感列表:电平敏感(用于组合电路)和边沿敏感(用于触发器)。下面的代码是相同的 2:1 Mux,但输出 y 现在是触发器输出。

always  @ (posedge clk )begin
    if (reset == 0) begin
      y <= 0;
    end 
    else if (sel == 0) begin
      y <= a;
    end 
    else begin
      y <= b;
    end
end    

        通常情况下我们不得不复位触发器,因此每次时钟从 0 转换到 1 (posedge) 时,都要检查复位是否有效(同步复位),然后继续执行正常逻辑。

        如果我们仔细观察,就会发现在组合逻辑的情况下,我们使用了“=”进行赋值;而对于时序逻辑,我们则使用了“<=”运算符。“=”是阻塞赋值,“<=”是非阻塞赋值。“=”在begin-end内顺序执行代码,而非阻塞“<=”则并行执行代码。

        我们可以写一个没有敏感列表的 always 块,在这种情况下需要有一个延迟,如下面的代码所示。

always  begin
    #5  clk = ~clk;
end

        语句前面的 #5 将其延迟 5 个时间单位。

Assign块

        assign 语句仅用于建模组合逻辑,并且会连续执行。所以赋值语句被称为“连续赋值语句(continuous assignment statement')”,因为其没有敏感列表。

  assign out = (enable) ? data : 1'bz;

        上面的例子是一个三态buffer。当使能为 1 时,数据被驱动到 out,否则 out 被拉到高阻抗。我们可以使用嵌套的条件运算符来构造多路选择器、解码器和编码器。

  assign out = data;

        这个例子是一个简单的buffer。

任务和函数

        当一次又一次重复相同的旧事物时,Verilog 与任何其他编程语言一样,提供了解决重复使用代码的方法----任务和函数。

        下面的代码可用于计算奇偶校验。

function parity;
    input [31:0] data;
    integer i;
    begin
      parity = 0;
      for (i= 0; i < 32; i = i + 1) begin
        parity = parity ^ data[i];
      end
    end
endfunction

        函数和任务具有相同的语法,一个区别是任务可以有时间延迟,但函数不能。这意味着函数可用于建模组合逻辑。第二个区别是函数可以返回一个值,而任务不能。


Testbench(测试脚本)

        好的,我们已经根据设计文档编写了代码,接着呢?

        我们需要对其进行测试,看看它是否符合规格。大多数时候,这与我们在大学时代在数字实验室中所做的相同----驱动输入,将输出与预期值相匹配。

        这是仲裁器的RTL代码:

module arbiter (
clock, 
reset, 
req_0,
req_1, 
gnt_0,
gnt_1
);

input clock, reset, req_0, req_1;
output gnt_0, gnt_1;

reg gnt_0, gnt_1;

always @ (posedge clock or posedge reset)
if (reset) begin
  gnt_0 <= 0;
  gnt_1 <= 0;
end else if (req_0) begin
  gnt_0 <= 1;
  gnt_1 <= 0;
end else if (req_1) begin
  gnt_0 <= 0;
  gnt_1 <= 1;
end

endmodule

        这是仲裁器的Testbench:

module arbiter_tb;

reg clock, reset, req0,req1;
wire gnt0,gnt1;

initial begin
  $monitor ("req0=%b,req1=%b,gnt0=%b,gnt1=%b", req0,req1,gnt0,gnt1);
  clock = 0;
  reset = 0;
  req0 = 0;
  req1 = 0;
   #5  reset = 1;
   #15  reset = 0;
   #10  req0 = 1;
   #10  req0 = 0;
   #10  req1 = 1;
   #10  req1 = 0;
   #10  {req0,req1} = 2'b11;
   #10  {req0,req1} = 2'b00;
   #10  $finish;
end

always begin
  #5  clock =  ! clock;
end

arbiter U0 (
.clock (clock),
.reset (reset),
.req_0 (req0),
.req_1 (req1),
.gnt_0 (gnt0),
.gnt_1 (gnt1)
);

endmodule

        看起来我们已经将所有仲裁器的输入声明为 reg,将输出声明为 wire;我们这样做是因为测试平台需要驱动输入并需要监控输出。

        在声明了所有需要的变量之后,我们将所有输入初始化为已知状态----这是在initial块中实现的。初始化后,我们按照要测试仲裁器的顺序断言/取消断言复位、req0、req1。时钟是用 always 块生成的。完成测试后,我们需要停止仿真----使用了 $finish 来终止仿真。$monitor 则用于监空信号列表的变化,并以我们想要的格式打印出来。

        这是仿真运行后的结果:

req0=0,req1=0,gnt0=x,gnt1=x
req0=0,req1=0,gnt0=0,gnt1=0
req0=1,req1=0,gnt0=0,gnt1=0
req0=1,req1=0,gnt0=1,gnt1=0
req0=0,req1=0,gnt0=1,gnt1=0
req0=0,req1=1,gnt0=1,gnt1=0
req0=0,req1=1,gnt0=0,gnt1=1
req0=0,req1=0,gnt0=0,gnt1=1
req0=1,req1=1,gnt0=0,gnt1=1
req0=1,req1=1,gnt0=1,gnt1=0
req0=0,req1=0,gnt0=1,gnt1=0

  • 📣您有任何问题,都可以在评论区和我交流📃!
  • 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵,博客主页:wuzhikai.blog.csdn.net
  • 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!

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

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

相关文章

Java反射面试总结(二)

为什么引入反射概念&#xff1f;反射机制的应用有哪些&#xff1f; 我们来看一下 Oracle 官方文档中对反射的描述&#xff1a; 从 Oracle 官方文档中可以看出&#xff0c;反射主要应用在以下几方面&#xff1a; 反射让开发人员可以通过外部类的全路径名创建对象&#xff0c;…

详解C语言结构体内存对齐:你知道如何快速计算结构体大小吗?

本篇博客会讲解C语言结构体的内存对齐&#xff0c;并且给出一种快速计算结构体大小的方式。主要讲解下面几点&#xff1a; 结构体的内存对齐是什么&#xff1f;如何快速计算结构体的大小&#xff1f;如何利用内存对齐节省结构体占用的内存空间&#xff1f;为什么结构体要内存对…

分布式数据库架构路线大揭秘

文章目录分布式数据库是如何演进的&#xff1f;数据库与分布式中间件有什么区别&#xff1f;如何处理分布式事务&#xff0c;提供外部一致性&#xff1f;如何处理分布式SQL&#xff1f;如何实现分布式一致性&#xff1f;数据库更适合金融政企的未来这些年大家都在谈分布式数据库…

MySQL-中间件mycat(一)

目录 &#x1f341;mycat基础概念 &#x1f341;Mycat安装部署 &#x1f343;初始环境 &#x1f343;测试环境 &#x1f343;下载安装 &#x1f343;修改配置文件 &#x1f343;启动mycat &#x1f343;测试连接 &#x1f990;博客主页&#xff1a;大虾好吃吗的博客 &#x1f9…

边缘网关thingsboard-gateway DTU902

thingsboard-gateway是一个采用python语言编写的开放源代码网关程序&#xff0c;用于将传统或第三方系统的设备与thingsboard平台连接。 支持 采集Modbus slaves、CAN、MQTT 、OPC-UA servers, Sigfox Backend。 除了具备普通 网关外&#xff0c;还具备可配置的边缘能力&…

rabbitmq深入实践

生产者&#xff0c;交换机&#xff0c;队列&#xff0c;消费者 交换机和队列通过 rounting key 绑定者&#xff0c;rounting key 可以是#.,*.这类topic模式&#xff0c; 生产者发送消息内容 rountingkey&#xff0c; 到达交换机后交换机检查与之绑定的队列&#xff0c; 如果能匹…

Yolov5之common.py文件解读

深度学习训练营原文链接前言0.导入需要的包以及基本配置1.基本组件1.1 autopad1.2 ConvDWConv模块1.3TransformerLayer模块1.4 Bottleneck和BottleneckCSPBottleneck模型结构1.5 CrossConv模块1.6 C3模块基于C3的改进1.7SPP1.8Focus模块1.9 Concat模块1.10 Contract和Expand1.1…

好东西!!!多亏几位大牛整理的面试题,让我成功上岸!!

凡事预则立&#xff0c;不预则废。相信很多程序员朋友在跳槽前都会临阵磨枪&#xff0c;在网络上搜集一些面试题进行准备。 然而&#xff0c;当机会来临时&#xff0c;却发现这些面试题往往“不快也不光”.... 由于Java面试涉及的范围很广&#xff0c;很杂&#xff0c;而且技…

使用MyBatis实现简单查询

文章目录一&#xff0c;创建数据库与表&#xff08;一&#xff09;在Navicat里创建MySQL数据库testdb&#xff08;二&#xff09;创建用户表 - t_user&#xff08;三&#xff09;在用户表里插入3条记录二&#xff0c;案例演示MyBatis基本使用&#xff08;一&#xff09;创建Mav…

解决idea每次打开新的项目都需要重新配置maven

原理&#xff1a;就是通过 idea 来进行全局配置【非当前工程配置】 IDEA 版本&#xff1a;2023.1 如何查看版本信息 &#xff1f; 【主菜单】——【帮助】——【关于】 我在网上查找了许多文章 &#xff0c;我混淆了一点&#xff01;当前工程的设置 & 全局设置 不在一个地方…

马斯克掷重金收购英

人前主义&#xff0c;人后生意。在带领一众科技圈大佬签署了呼吁暂停研发比GPT-4更强AI模型的公开信后不久&#xff0c;马斯克却转头豪掷千金收购了10000块英伟达GPU。 一些网友吐槽&#xff0c;以马老板的格局而言&#xff0c;这次价值过亿的投资绝对不是为了借着AI概念火爆来…

2021年 团体程序设计天梯赛——题解集

Hello各位童学大家好&#xff01;&#x1f60a;&#x1f60a;&#xff0c;茫茫题海你我相遇即是缘分呐&#xff0c;或许日复一日的刷题已经让你感到疲惫甚至厌倦了&#xff0c;但是我们真的真的已经达到了我们自身极限了吗&#xff1f;少一点自我感动&#xff0c;没有结果前别太…

[FREERTOS] 任务的创建、删除、调度与状态

1.什么是任务&#xff1f; 我的理解是&#xff1a;任务像是进程/线程&#xff0c;创建一个任务就会开辟一个空间&#xff0c;每一个任务都是独立的执行相应的动作互不干扰&#xff0c;就比如玩游戏&#xff0c;陪女朋友&#xff0c;任务通常都会有一个while(1)死循环 2.与任务创…

使用cloudflare代理flask启用https服务

原文来自&#xff1a;使用cloudflare代理flask启用https服务 | 夜空中最亮的星 欢迎大家留言讨论 问题1&#xff1a;使用cloudflare的dns回源服务器的时候&#xff0c;出现了http和https不断反复重定向 问题2: flask只能启用http服务&#xff0c;需要启用https 步骤 服务器&…

浅谈[Linux搭建GitLab私有仓库,并内网穿透实现公网访问]

转载自远控源码文章&#xff1a;Linux搭建GitLab私有仓库&#xff0c;并内网穿透实现公网访问 前言 GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具&#xff0c;并在此基础上搭建起来的Web服务。 Gitlab是被广泛使用的基于git的开源代码管理平…

报错解决:Python ‘NoneType‘ object is not subscriptable , 获取到的数据为None,需要保留数据

人生苦短&#xff0c;我用python 爬取某DB电影数据的时候&#xff0c; 在获取内容的时候出现 NoneType object is not subscriptablePython 资料报错交流:点击此处跳转文末名片获取 获取数据的部分代码是&#xff1a; writer_avatars (writers_list[wi][avatars][small]) …

Linux0.11 信号(十二)

系列文章目录 Linux 0.11启动过程分析&#xff08;一&#xff09; Linux 0.11 fork 函数&#xff08;二&#xff09; Linux0.11 缺页处理&#xff08;三&#xff09; Linux0.11 根文件系统挂载&#xff08;四&#xff09; Linux0.11 文件打开open函数&#xff08;五&#xff09…

前端webpack项目性能优化——体积压缩和compression-webpack-plugin的使用

前端webpack项目性能优化——体积压缩和compression-webpack-plugin的使用需求优化结果需求 脚手架搭建的项目&#xff0c;会默认开启sourceMap&#xff0c;此时打包下来的包会很大&#xff0c;如图&#xff1a;map文件比所有js文件都大&#xff0c;会导致包整体体积过大&…

NIFI大数据进阶_Json内容转换为Hive支持的文本格式_操作方法说明_01_EvaluteJsonPath处理器---大数据之Nifi工作笔记0031

然后我们再来看一下如何把json内容,转换成hive支持的文本格式,其实还是一个格式转换对吧 首先看一下用到的处理器,可以看到这里我们用到了evaluateJsonPath处理器,这个处理器用来提取json中的熟悉,然后ReplaceText处理器用来替换掉FlowFile中的属性的内容 首先看一下这个Evalua…

【Python-Conda】Conda操作解读 pip 和 conda 的区别

【Python-Conda】Conda操作解读 & conda与pip的区别 文章目录【Python-Conda】Conda操作解读 & conda与pip的区别1. 介绍2. conda 操作2.1 创建环境2.2 查看conda已创建的环境2.3 删除环境2.3.1 删除虚拟环境中的包2.4 激活&#xff08;失活&#xff09;环境2.4.1 激活…