SV学习——数据类型(第二章)

这篇具有很好参考价值的文章主要介绍了SV学习——数据类型(第二章)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

verilog有1995和2001版本,而SV是verilog的延伸,SV发布的时候直接就是3.0,之后可能不再存在verilog,而是统一用SV。SV是完全兼容verilog的。verilog文件以.v结尾,SV文件以.sv结尾。语法是全部兼容的,SV是verilog的扩展和延伸。

Verilog中如何区分reg和wire?

verilog中有reg和wire两种数据类型,都是四值逻辑 0,1,x,z。什么时候用wire?verilog作为硬件描述语言,认为哪些变量应被实现为寄存器,哪些变量被实现为线网类型,有利于后端综合。什么时候用reg?

  • wire的本质是一条没有逻辑的连线,输入是什么输出就是什么。wire型数据常用来表示以assign关键字指定的组合逻辑信号。默认初始值为z(高阻状态)
  • reg型表示的寄存器类型,用于always模块内被赋值的信号,必须定义为reg型,代表触发器,常用于时序逻辑电路,(也可以用于always块表示的组合逻辑)reg相当于存储单元,默认初始值是x(未知状态)

从仿真的角度来说,HDL语言面对的是编译器(如Modelsim等),相当于软件思路。

  1. wire对应于连续赋值,如assign
  2. reg对应于过程赋值,如always,initial

从综合的角度来说,HDL语言面对的是综合器(如DC等),要从电路的角度来考虑。

  1. wire型的变量综合出来一般是一根导线;
  2. reg变量在always块中有两种情况:
  • always后的敏感表中是(a or b or c)形式的,也就是不带时钟边沿的,综合出来是组合逻辑
  • always后的敏感表中是(posedge clk)形式的,也就是带边沿的,综合出来一般是时序逻辑,会包含触发器(Flip-Flop)

总结:

  • 使用assign连续赋值语句,必须是wire类型,wire只能用在assign中,只能用于组合逻辑
  • 在always语句块中,只能是reg类型,综合出来可能是组合逻辑,也可能是时序逻辑。
  • 在initial语句块中,只能是reg类型

SV中的数据类型

如何理解SV中的logic类型?

SV中logic为四值逻辑,0,1,x,z。logic可以在大多数情况下,代替reg和wire类型,也就是说logic类型的变量既可以用assign连续赋值,也可以出现在always和initial语句块中。

在验证过程中,主要是驱动和连接硬件模块,不用去考虑变量被综合成什么,使用logic可以不用考虑是用reg还是wire,减少了出错的机会。

但是对于有多个驱动的情况,不能使用logic类型,只能使用wire类型。logic类型的变量只能接受单一驱动。

logic如果有多个驱动会怎样?

会出现error!

新的数据类型

sv中引入了双状态数据类型,可以用来减少内存使用量,提升仿真性能。

四值逻辑:0,1,x,z  默认为x,               integer,reg,logic,wire,tri

二值逻辑:0,1  默认为0,                          byte,shortint,int,longint,bit

有符号类型: byte,shortint,int,longint,integer

无符号类型: bit,logic,reg,wire,tri

四值逻辑转化成二值逻辑,x,z会变为0。

定宽数组

一维定宽数组

int a[3:0]        //声明一个宽度为4,名为a的数组,建议用这种方式命名
int a[0:3]        //a这个数组里面的元素是int类型
int a[4]          //也可以这样写  SV中允许只给出数组宽度的便捷声明方式

建议用第一种方式命名数组,如果没有声明数据的位宽,默认存放数据的位宽是1吗?

a[0]
a[1]
a[2]
a[3]

多维定宽数组

int array [7:0][3:0]   //声明8行4列的二维数组,前面是行,后面是列
int array3 [8][4]       //紧凑型声明
array [7][3]           //设置最后一个元素
array[0][0] array[0][1] array[0][2] array[0][3]
array[1][0] array[1][1] array[1][2] array[1][3]
array[2][0] array[2][1] array[2][2] array[2][3]
array[3][0] array[3][1] array[3][2] array[3][3]
array[4][0] array[4][1] array[4][2] array[4][3]
array[5][0] array[5][1] array[5][2] array[5][3]
array[6][0] array[6][1] array[6][2] array[6][3]
array[7][0] array[7][1] array[7][2] array[7][3]

问题:数组的命名方式对数组有什么影响?

关于bit和byte

  • Bit 意为"位"或"比特",是计算机运算的基础单位;Byte 意为"字节",是计算机文件大小的基本计算单位;
  • 1 bit 就是1位二进制数,比如 1 或者 0;1 Byte 就是 1 个字节,1 个字节是由 8 个二进制位组成的。比如1111111,00000000等。
  • Bit 只能代表一个内容,(0或者1,其中的一个),1 Byte 代表 256 个内容 = 2 的 8 次方。
  • 一个 bit 就是一个二进制数的最小单元,就像我们说我们能够拥有的最小金额的钱就是一分一样,只不过比特是在二进制数中罢了。如果将 8 个 0 或者 1 组合在一起,例如 0110 1010(在中间加一个空格是为了看起来清晰明了),就可以说它们是八个比特或者说一个字节。

常量数组

用一个单引号加大括号来初始化数组

int ascend[4] = '{0,1,2,3};     //对四个元素进行初始化
int descend[5];                //定义宽度为5的数组


descend = '{4,3,2,1,0};         //对5个元素赋值
descend[0:2] = '{5,6,7};        //对前三个元素赋值
ascend = '{4{8}};               //四个值全都为8
descend = '{9,8,default:1};     //剩下的缺省元素设置默认值
 

对合并数组和非合并数组的理解

首先一定要清楚什么样的命名方式是合并数组?什么样的是非合并数组?

数组索引,先定是哪一个抽屉,然后看是第几个字节组,然后看数的第几位 

在数组名之后的是这个数组里可以放几个数据,数组名之前的位宽表示每个数据是几位的!注意:合并数组和非合并数组的声明方式是不一样的

非合并数组的声明


/***********************************************
//非合并数组
bit [7:0] b_unpack[3]            //数组里有三个数,每个数的位宽是8
//所以,数组就像抽屉

b_unpack[2][3]                  //索引时先索引是第几个抽屉,然后再看数的第几位

SV学习——数据类型(第二章),SV基础知识学习总结,学习,fpga开发

对于非合并数组,就像在抽屉里放东西,规定了有三个抽屉,可以放三个数,每个抽屉最长可以放32位宽的数,但是现在放进来的数,每个只有8位宽,三个数各占一个抽屉,而且都没有把抽屉填满。非合并数组的方式,会浪费空间,抽屉有很大一部分没用到,

合并数组的声明

希望既可以把它当作为一个整体来访问,也可以把它分解成更小的单元。它既可以用作数组,也可以当成单独的数据,与非合并数组不同的是,它的存放方式是连续的比特集合,中间没有闲置的空间。在非合并数组中,字的低位用来存放数据,高位则不使用。

 bytes是一个具有4个字节的合并数组,以单字形式存放

bit[3:0][7:0] bytes;                //4个字节组装成32bit
bytes = 32'hCafe_Dada;
$displayh (bytes,,                  //显示所有的32比特
           bytes[3],,               //最高字节CA
           bytes[3][7];             //最高比特位“1”

SV学习——数据类型(第二章),SV基础知识学习总结,学习,fpga开发

合并/非合并数组(mixed arrays)的声明

  • barray则是一个具有3个类似bytes元素的数组
  • 使用一个下标可以得到一个字的数据barray[0],
  • 使用两个下标,可以得到一个字节的数据barray[0][3],
  • 使用三个下标,可以访问到单个比特位barray[0][1][6]。
  • 数组声明中在变量名后面指定了数组的大小,barray[3],这个维度是非合并的,所以在使用该数组时至少要有一个下标。
bit[3:0][7:0]barray[3]         //合并:3x32比特
bit[31:0] 1w = 32'h0123_4567   //字
bit[7:0][3:0] nibbles;         //合并数组
barray [0] = 1w;               //把1w的值给了barray[0]
barray [0][3] = 8'h01;          
barray [0][1][6] = 1'b1;
nibbles = barray[0]
bit [3:0][7:0] barray[3];          //每个抽屉里放了四个8位宽的数,有三个这样的抽屉

//数组名前面的声明必须都是[mab:lab],不能是[size]

SV学习——数据类型(第二章),SV基础知识学习总结,学习,fpga开发


合并数组和非合并数组的选择

  • 需要和标量相互转换时,使用合并数组会非常方便
  • 如果需要等待数组中的变化,则必须使用合并数组,当测试平台需要通过存储器数据的变化来唤醒时,会想到使用@操作符,但这个操作符只能用于标量或者合并数组,

数组操作,for和foreach

一维数组的循环遍历

initial begin
    bit [31:0] src[5], dst[5];
    for (int i=0;i<$size(src);i++)       //for就是正常的for循环
        src[i] = i;
    foreach (dst[j])
        dst[j] = src[j]*2;               //foreach自动遍历数组中的所有元素
end

多维数组的循环遍历

int md[2][3] = '{'{0,1,2},'{3,4,5}};
initial begin
    $display("initial value:");
    foreach (md[i,j])                
//foreach对多维的遍历并不是写成md[i][j],而是写在一个方括号里,用逗号隔开
       $display("md[%0d][%0d] = %0d",i,j,md[i][j])     



$display("new value:");
// 对最后三个元素赋值5
md = '{'{9,8,7},'{3{32'd5}}};
foreach (md[i,j])
    $display("md[%0d][%0d]=%0d",i,j,md[i][j]);
end                         

foreach会遍历原始声明中的数组范围

f[5] = f[4:0],而foreach(f[i]) 等同于for(i=0;i<=4;i++)

对于数组rev[6:2],foreach(rev[i])等同于for(int i = 6; i>=2; i--) 

数组的复制和比较

initial begin
    bit[31:0] src[5] = '{0,1,2,3,4};
              dst[5] = '{5,4,3,2,1};
    if (src == dst)
        $display("src==dst");
    else
        $dspllay("src!=dst");

    //把src所有元素值复制给src
    dst = src;
    //只改变一个元素的值
    src[0] = 5;

//第二种方法,用比较运算符
    $display("src %S dst",(src==dst)? "==":"!=");

//使用数组片段对第1-4个元素进行比较
    $display("src[1:4] %s dst[1:4]",(src[1:4]==dst[1:4])? "==":"!=");
end

同时使用位下标和数组下标

initial begin
    bit[31:0]src[5] = '{5{5}};
    $display src[0],,                //'b101
             src[0][0],,             //'b1
             src[0][2:1];            //'b10      第一个是数组下标,第二个是位下标

动态数组

如何声明?

如何赋值?

定宽数组的宽度在编译时就已经确定了,使用定宽数组时数组的宽度需要达到可以容纳的最大事务量,但实际的事务量可能会远远小于最大值,这就造成了存储空间的浪费,

SV中提供了动态数组,可以在仿真时分配空间或调整宽度,这样就可以使用最小的存储

  • 声明时用 []
  • 分配空间时用new[]
int dyn[],d2[];              //声明动态数组
initial begin
    dyn = new[5]             //分配5个元素
    foreach (dyn[j]) dyn[j] = j ;      //给元素赋值
    d2 = dyn;                // 复制一个动态数组
    d2[0] = 5;
    $display(dyn[0],d2[0]);
    dyn = new[20](dyn);
    dyn = new[100]
    dyn.delete();
end

系统函数$size的返回值是数组的宽度,动态数组中有内建子程序,如delete,size

队列

队列结合了链表和数组的优点。队列的声明使用[$]

  • 队列与链表相似,可以在一个队列中的任何地方增加或删除元素,在性能上的损失比动态数组小的多,因为动态数组需要分配新的数组并复制所有元素的值。
  • 队列与数组相似,可以通过索引实现对任一元素的访问,而不需要像链表那样去遍历目标元素之前的所有元素。
  • 可以在任何地方增加或删除元素,也可以使用索引
int j = 1,
    q2[$] = {3,4}               //{3,4} 队列的声明,赋值不需要使用'
    q[$]  = {0,2,5}             //{0,2,5}

initial begin
    q.insert(1,j);             //{0,1,2,5} 在索引数为1的位置之前插入元素1
    q.insert(3,q2);            //{0,1,2,3,4,5} 在5之前插入一个队列q2
    q.delete(1);               //{0,2,3,4,5} 删除第二个元素       

    q.push_front(6);            // {6,0,2,3,4,5}  在队列最前面插入元素6
    j = q.pop_back;             // {6,0,2,3,4}   j=5
    q.push_back(8);             // {6,0,2,3,4,8} 在队列末尾插入元素8
    j = q.pop_front;            // {0,2,3,4,8}   j=6
    foreach(q[i])
        $display(q[i]);         //打印整个队列
    q.delete();
end
  • 队列中的元素是连续存放的,所以在队列的前面或后面存放数据非常方便
  • 在队列中增加或删除元素需要对已经存在的数组进行搬移以腾出空间。队列越长,越耗时

关联数组

针对超大容量的数组,假如正在对一个有着几个G字节寻址范围的处理器进行建模,在典型的测试中,这个处理器可能只访问了用来存放可执行代码和数据的几百或几千个字节,这种情况下对几个G字节的存储空间进行分配和初始化显然是浪费的。

SV提供了关联数组类型,当对一个非常大的地址空间进行寻址时,SV只为实际写入的元素分配空间。仿真器使用 树或哈希表的形式来存放关联数组。

SV学习——数据类型(第二章),SV基础知识学习总结,学习,fpga开发

bit[63:0] assoc[bit[63:0]],idx = 1;           //联合数组的声明

repeat (64) begin
    assoc[idx] = idx;
    idx = idx << 1;
end

//使用foreach遍历数组
foreach (assoc[i])
    $display("assoc($h)=%h",i,assoc[i]);


//找到并删除第一个元素
assoc.first[idx];
assoc.delete(idx);

链表(SV中避免使用)

数组定位方法

在非合并数组中查找数据,可以使用数组定位方法,这些方法的返回值通常是一个队列 

min() 找出数组中最小值

max() 找出数组中最大值

unique() 返回的是数组中具有唯一值的队列,去掉重复的值

int f[6] = '{1,6,2,6,8,6};
int d[] = '{2,4,6,8,10};
int q[$] = {1,3,5,7,tq[$]};

tq = q.min();     // 1 
tq = d.max();     // 10
tq = f.unique;    // {1,6,2,8}

定位方法:find

int d[] = '{9,1,8,3,4,4},tq[$]; 
//找出所有大于3的元素
tq = d.find with (item>3);    //{9,8,4,4}

//等效代码
tq.delete();
foreach (d[i])
    if (d[i]>3)
        tq.push_back (d[i]);

tq = d.find_index with (item>3)      //找到所有大于3的元素的索引值
tq = d.find_first with (item>99)     //找到大于99的第一个元素
tq = d.find_first_index_with (item==8)     //找到等于8的第一个元素的索引值
tq = d.find_last with (item == 4)         //找到值等于4的最后一个元素
tq = d.find_last_index with (item ==4);    //找到等于4的最后一个元素的索引值

条件语句with中,item被称为重复参数,代表了数组中一个单独的元素,item是缺省的名字,你也可以指定别的名字,只要在数组方法的参数列表中列出来就可以了.

以下四种方法的表示是等价的

tq = d.find_first with (item ==4);
tq = d.find_first() with (item == 4);
tq = d.find_first(item) with (item == 4);

tq = d.find_first(x) with (x == 4);

数组排序方法

int d[] = '{9,1,8,3,4,4};
d.reverse();               //翻转数组 {4,4,3,8,1,9}
d.sort();                  //从小到大排列数组 {1,3,4,4,8,9}
d.rsort();                 //从大到小排列数组 {9,8,4,4,3,1}
d.shuffle();               //数组中元素随机排列 {9,8,1,4,3,4}

排序方法修改了原始数组,而数组定位方法新建了一个队列来保存结果

选择存储类型(数组类型的选择)

四大数组类型:定宽数组、动态数组、关联数组、队列

需要考虑的因素:

  • 位宽
  • 内存
  • 读取速度
  • 排序
  • 文件类型,文件结构

数组的位宽

  • 如果数组的索引是连续的整数0,1,2,3等,则应该使用定宽或动态数组
  • 当数组的宽度在编译时已经确定时,选择定宽数组
  • 程序运行时才知道数组宽度选择动态数组
  • 长度可变的数据包使用动态数组
  • 编写处理数组的子程序时,使用动态数组可以处理不同宽度的数组。带有定宽数组参数的子程序只能接受指定宽度的数组。
  • 数组索引不规则时,选择关联数组
  • 关联数组也可以用来对基于内容寻址的存储器建模
  • 仿真过程中元素数目变化很大的数组,队列是一个很好的选择

存储器用量

  • 使用双状态数据类型可以减少仿真时的存储器用量
  • 为了避免浪费空间,应尽量选择32比特的整数倍作为数据位宽

读取速度

  • 还应根据每个时钟周期内的存取次数来选择数据类型
  • 定宽和动态数组都是被存放在连续的存储器空间里,所以访问其中的任何元素耗时都相同,而与数组的大小无关。
  • 队列的读写速度与定宽或动态数组基本相当,队列首尾元素的存取几乎没有任何额外开销,而在队列中插入或删除元素需要对其他元素进行挪移,这时会变得很慢
  • 关联数组需要搜索,存取速度最慢

使用typedef创建新的类型

所谓自定义类型就是用户用一个具有意义的词语去简单的描述一个很长,很难理解的量。并且这个新的词在SV语法里面是没有定义的。

//SV中用户自定义类型
parameter OPSIZE = 8;
typedef reg[OPSIZE-1:0] opreg_t;

opera_t op_a,op_b;

typedef int unsigned uint; 这就是uint对int unsigned的指代;

当再一次使用的时候,uint a,b; //定义的就是int unsigned类型。

typedef bit [31:0] uint;       //32bit双状态无符号数
typedef int unsigned uint;     //等效的定义

结构体类型(struct)

typedef经常和enum和struct一起使用,

enum来创建枚举类型;
struct来创建结构体类型。

typedef struct{int a;
               byte b;
               shorting c;
               int d;} my_struct_s;

my_struct_s st = '{32'haaaa_aaaad,
                   8'hbb,
                   16'hcccc,
                   32'hdddd_dddd};

$display(st.a,st.b,st.c,st.d)    

枚举类型(ENUM)

一个简单的枚举类型

enum {RED,BLUE,GREEN} color;

最简单的枚举类型声明包含了一个常量名称列表以及一个或多个变量,通过这种方式创建的是一个匿名的枚举类型,只能用于这个例子中声明的变量

创建一个署名的枚举类型有利于声明更多新变量,尤其是当这些变量被用作子程序参数或者模块端口时,你需要首先创建枚举类型,然后再创建相应的变量。

对于一个状态机,进行状态跳转的时候,以前可能是利用参数化的方法表明状态:

parameter INIT   =2'b00;
parameter DECODE =2'b01;
parameter IDLE   =2'b10;

现在我们可以用枚举类型的数据变量来更方便的表示不同的状态

typedef enum {INIT, DECODE, IDLE} fsmstate_e;      //默认INIT=0,DECODE=1,IDLE=2
fsmstate_e pstate, nstate;                         // 声明自定义类型变量
……
case (pstate)
    IDLE: nstate = INIT;                           // 数值赋值
    INIT: nstate = DECODE;
    default: nstate = IDLE;
endcase
$display(“Next state is %s”, nstate.name());       // 显示状态名,如果不用.name,则显示编号:0/1/2

使用后缀_e 来表示枚举类型,定义的枚举变量会默认为其中的元素赋值,当然我们也可以自定义其中的数值:

typedef enum {A=1,B,C,X=35,Y,Z} fsmstate_e;//此时A=1,B=2,C=3,X=35,Y=36,Z=37

枚举变量有且仅有一个数值。  

SV学习——数据类型(第二章),SV基础知识学习总结,学习,fpga开发

 枚举类型会被默认当成int类型存储,int类型的默认值是0,

SV学习——数据类型(第二章),SV基础知识学习总结,学习,fpga开发

枚举类型的转换

枚举类型的默认值是二值的int,可以直接将枚举变量直接赋值给int,但是int不能直接赋值给枚举变量,需要做一个cast转换, 

字符串

所有与字符串相关的处理,都使用string来保存和处理。 与字符串处理相关的还包括字符串的格式化函数。文章来源地址https://www.toymoban.com/news/detail-687999.html

到了这里,关于SV学习——数据类型(第二章)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 静态时序分析 第二章 基础知识

    目录 1. 逻辑门单元 2. 门单元的时序计算参数         2.1 信号转换延时(transition delay)          2.2 逻辑门延时(logic gate delay) 3.  时序单元相关约束         3.1 建立时间(setup time)         3.2 保持时间(hold time)         3.3 恢复时间         3.4 移除时间      

    2023年04月13日
    浏览(47)
  • 第二章 嵌入式系统硬件基础知识

    (1)信号特性 用 “ 逻辑真 ” “ 1 ” 或 “ 确定 ”来表示 高电平 用 “ 逻辑假 ” “ 0 ” 或 “ 不确定 ”来表示 低电平 1和0称为 互补信号 (2)信号转换 1、数字集成电路的分类         按照开关元件的不同,数字集成电路分为两大类:一类是 双极型集成电路

    2024年01月21日
    浏览(71)
  • 第二章:计算机系统基础知识之计算机网络

      计算机网络是利用通信线路将地理上分散的、具有独立功能的计算机系统和通信设备按不同的形式连接起来,并依靠网络软件及通信协议实现资源共享和信息传递的系统。   计算机网络技术主要涵盖 通信技术、网络技术、组网技术和网络工程 等四个方面。 数据通信

    2024年04月27日
    浏览(55)
  • C++第二章:变量和基本内置类型

    C++定义了一套包括算数类型和空类型在内的基本数据类型。 其中算数类型包含了字符、整形数、布尔值、浮点数。空类型不对应具体的值,仅用于一些特殊的场合,例如最常见的是:当一个函数不返回任何值时使用空类型作为返回类型。 算数类型分为两种:整形(包括字符和

    2024年02月06日
    浏览(42)
  • 第二章——开始学习C++

    进入C++ 首先介绍一个显示消息的简单C++程序(不同于以往的“Hello,world\\\")     注意看这里的文件名,是以.cpp 结尾的。这是C++程序常用的方式 在其他环境中可能需要不同的扩展名  另外C++对大小写敏感,也就是说严格区分大写字母和小写字母。 通过上面这一小段程序来分

    2024年02月13日
    浏览(39)
  • 《Spring揭秘》-第二章- 学习记录

    IoC全称为Inversion of Control,中文翻译为控制反转,同时还有一个别名叫 依赖注入DI(Dependency Injection)。大多将IoC与DI看作同等概念,也有部分观点认为 依赖注入可以看作IoC的一种实现方式。 在没有Spring的时候,当我们需要依赖某个类或服务时,一般通过new创建一个对象(或者通

    2023年04月11日
    浏览(78)
  • 【考研数学】概率论与数理统计 —— 第二章 | 一维随机变量及其分布(1,基本概念与随机变量常见类型)

    暑假接近尾声了,争取赶一点概率论部分的进度。 设随机试验 E E E 的样本空间为 Ω Omega Ω , X X X 为定义于样本空间 Ω Omega Ω 上的函数,对于任意 w ∈ Ω w in Omega w ∈ Ω ,总存在唯一确定的 X ( w ) X(w) X ( w ) 与之对应,称 X ( w ) X(w) X ( w ) 为随机变量,一般记为 X X X 。 随机

    2024年02月11日
    浏览(46)
  • JS深入学习笔记 - 第二章.类和对象

    3.1面向对象 这里顺带提一句学习JAVA时,老师说的面向对象和面向过程的区别: 面向过程:强调做什么事情,具体什么步骤。举个把大象放进冰箱的例子: 打开冰箱门 把大象放进冰箱 关上冰箱门 面向对象: 强调的是做动作的主体(称之为对象) 冰箱 :打开操作 冰箱 :放

    2024年02月08日
    浏览(52)
  • 第二章-数据传输安全

    VPN虚拟专用网 :在ISP运营商公用网络中搭建专用的安全数据通道 VPN :隧道 – 封装技术 常见VPN :IPSec VPN、MPLS VPN、GRE VPN、SangFor VPN、PPTP VPN、L2TP VPN / L2F VPN 1)按应用场景分(业务类型) Client-LAN VPN(access VPN)客户端到网络:PPTP VPN、L2TP VPN / L2F VPN、SSL VPN、IPSec VPN LAN-LAN V

    2024年01月23日
    浏览(50)
  • dx12 龙书第二章学习笔记 -- 矩阵代数

    1.矩阵及其运算 矩阵的运算 :①加②减③标量乘法 ④矩阵乘法: 矩阵乘法要有意义的条件是矩阵A的列数和矩阵B的行数必须相同,所以一般不满足交换律 ⑤转置矩阵: ⑥矩阵行列式:det A 学习行列式的主要目的是:利用它推导出求逆矩阵的公式 方阵A是可逆的,当且仅当det

    2024年02月11日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包