您现在的位置是:主页 > news > TP5.1做的网站首页被挂马原因/白百度一下你就知道
TP5.1做的网站首页被挂马原因/白百度一下你就知道
admin2025/4/30 1:06:16【news】
简介TP5.1做的网站首页被挂马原因,白百度一下你就知道,深圳一元购网站设计公司,武汉哪家网站建设公司好主体内容摘自:https://blog.csdn.net/qq_34291505/article/details/87714172 一、端口 类似于verilog中的input、output 1、定义端口列表 定义一个模块前一定要先定义好端口。整个端口列表是由方法“IO[T <: Data](iodef: T)”来定义的,通常其参数…
主体内容摘自:https://blog.csdn.net/qq_34291505/article/details/87714172
一、端口
类似于verilog中的input、output
1、定义端口列表
定义一个模块前一定要先定义好端口。整个端口列表是由方法“IO[T <: Data](iodef: T)”来定义的,通常其参数是一个Bundle类型的对象,而且引用的字段名称必须是“io”。因为端口存在方向,所以还需要方法“Input[T <: Data](source: T)”和“Output[T <: Data](source: T)”来为每个端口表明具体的方向。注意,“Input[T <: Data](source: T)”和“Output[T <: Data](source: T)”仅仅是复制它们的参数,所以不能是已经被硬件类型包裹的数据类型。目前Chisel还不支持双向端口inout,只能通过黑盒里的Analog端口来模拟外部Verilog的双向端口。
一旦端口列表定义完成,就可以通过“io.xxx”来使用。输入可以驱动内部其它信号,输出可以被其他信号驱动。可以直接进行赋值操作,布尔类型的端口还能直接作为使能信号。端口不需要再使用其它硬件类型来定义,不过要注意从性质上来说它仍然属于组合逻辑的线网。例如:
class MyIO extends Bundle {val in = Input(Vec(5, UInt(32.W)))val out = Output(UInt(32.W))
}......val io = IO(new MyIO) // 模块的端口列表
......
2、翻转端口列表的方向
对于两个相连的模块,可能存在大量同名但方向相反的端口。仅仅为了翻转方向而不得不重写一遍端口显得费时费力,所以Chisel提供了“Flipped[T <: Data](source: T)”方法,可以把参数里所有的输入转输出,输出转输入。如果是黑盒里的Analog端口,则仍是双向的。例如:
class MyIO extends Bundle {val in = Input(Vec(5, UInt(32.W)))val out = Output(UInt(32.W))
}......val io = IO(new MyIO) // in是输入,out是输出
......val io = IO(Flipped(new MyIO)) // out是输入,in是输出
3、整体连接
翻转方向的端口列表通常配合整体连接符号“<>”使用。该操作符会把左右两边的端口列表里所有同名
的端口进行连接,而且同一级的端口方向必须是输入连输出、输出连输入,父级和子级的端口方向则是输入连输入、输出连输出。注意,方向必须按这个规则匹配,而且不能存在端口名字、数量、类型不同的情况。这样就省去了大量连线的代码。例如:
class MyIO extends Bundle {val in = Input(Vec(5, UInt(32.W)))val out = Output(UInt(32.W))
}......val io = IO(new Bundle {val x = new MyIO val y = Flipped(new MyIO)})io.x <> io.y // 相当于 io.y.in := io.x.in; io.x.out := io.y.out
......
注1:
<> 符号不是只可以连接包含有相同名称端口(x和y的端口名都是in和out)的Bundle对象,也可以连接普通的Input和Output端口,例如
io.y.in <> io.x.in
和io.x.out <> io.y.out
。只不过,只有前者才能体现出它的作用。
4、动态修改端口
- 方法①:使用可选字段
Chisel通过引入Scala的Boolean参数、可选值以及if语句
可以创建出可选的端口,在例化该模块时可以通过控制Boolean入参来生成不同的端口。例如:
class HalfFullAdder(val hasCarry: Boolean) extends Module {val io = IO(new Bundle {val a = Input(UInt(1.W))val b = Input(UInt(1.W))val carryIn = if (hasCarry) Some(Input(UInt(1.W))) else Noneval s = Output(UInt(1.W))val carryOut = Output(UInt(1.W))})val sum = io.a +& io.b +& io.carryIn.getOrElse(0.U)io.s := sum(0)io.carryOut := sum(1)
}
注意,端口应该包装成可选值,这样不需要端口时就能用对象None代替,编译出来的Verilog就不会生成这个端口。在给可选端口赋值时,应该先用可选值的get方法把端口解放出来。这里也体现了可选值语法的便利性。
- 方法②:使用
Zero-Width
chisel允许数据的位宽为0。位宽为0的IO会从生成的Verilog中去除,当你试图使用位宽为0的wire时,你会得到一个常数0。如果0是一个合理的默认值,那么更推荐使用该方法。如下例所示:
class HalfFullAdder(val hasCarry: Boolean) extends Module {val io = IO(new Bundle {val a = Input(UInt(1.W))val b = Input(UInt(1.W))val carryIn = Input(if (hasCarry) UInt(1.W) else UInt(0.W))val s = Output(UInt(1.W))val carryOut = Output(UInt(1.W))})val sum = io.a +& io.b +& io.carryInio.s := sum(0)io.carryOut := sum(1)
}
注2:
如果端口定义成多维vec,那么在生成的verilog代码中,会将其展开为位宽相同、名字不同的端口。
chisel代码:
package simpleimport chisel3._
import chisel3.util._
import chisel3.Driverclass REG extends Module {val io = IO(new Bundle {val a = Input(Vec(3, UInt(8.W)))val en = Input(Bool())val c = Output(Vec(3, UInt(8.W)))})//val reg3 = Reg(Vec(3,Vec(480,Vec(480, UInt(8.W)))))val reg = Reg(Vec(3, UInt(8.W)))reg := io.aio.c := reg
} object REG extends App {(new chisel3.stage.ChiselStage).emitVerilog(new REG(), Array("--target-dir", "generated"))
}
生成的verilog代码:
module REG(input clock,input reset,input [7:0] io_a_0,input [7:0] io_a_1,input [7:0] io_a_2,input io_en,output [7:0] io_c_0,output [7:0] io_c_1,output [7:0] io_c_2
);
`ifdef RANDOMIZE_REG_INITreg [31:0] _RAND_0;reg [31:0] _RAND_1;reg [31:0] _RAND_2;
`endif // RANDOMIZE_REG_INITreg [7:0] reg_0; // @[reg_test.scala 17:16]reg [7:0] reg_1; // @[reg_test.scala 17:16]reg [7:0] reg_2; // @[reg_test.scala 17:16]assign io_c_0 = reg_0; // @[reg_test.scala 19:8]assign io_c_1 = reg_1; // @[reg_test.scala 19:8]assign io_c_2 = reg_2; // @[reg_test.scala 19:8]always @(posedge clock) beginreg_0 <= io_a_0; // @[reg_test.scala 18:7]reg_1 <= io_a_1; // @[reg_test.scala 18:7]reg_2 <= io_a_2; // @[reg_test.scala 18:7]end
endmodule
不仅如此,还可以看到chisel编译器会把Vec[T]展开为一维数据,而不是转换成verilog中的多维reg,这点需要注意!!!
5、使用DataMirror.modulePorts
检查模块的IOs,MultiIOModules, RawModules, 和BlackBoxes
都可以。
import chisel3.experimental.DataMirror
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}class Adder extends MultiIOModule {val a = IO(Input(UInt(8.W)))val b = IO(Input(UInt(8.W)))val c = IO(Output(UInt(8.W)))c := a +& b
}class Test extends MultiIOModule {val adder = Module(new Adder)// for debug onlyadder.a := DontCareadder.b := DontCare// Inspect ports of adder// See the result below.DataMirror.modulePorts(adder).foreach { case (name, port) => {println(s"Found port $name: $port")}}
}(new ChiselStage).execute(Array.empty, Seq(ChiselGeneratorAnnotation(() => new Test)))
// Elaborating design…
// Found port clock: Clock(IO clock in Adder)
// Found port reset: Reset(IO reset in Adder)
// Found port a: UInt<8>(IO a in Adder)
// Found port b: UInt<8>(IO b in Adder)
// Found port c: UInt<8>(IO c in Adder)
// Done elaborating.
// res0: firrtl.AnnotationSeq = firrtl.AnnotationSeq@6cb91155
二、模块
类似于verilog中的module…endmodule
Chisel在构建硬件的思路上类似Verilog。在Verilog中,是以“模块(module)”为基本单位组成一个完整的独立功能实体,所以Chisel也是按模块划分的,只不过不是用关键字“module”开头来定义模块,而是用一个继承自Module类的自定义class。
1、定义模块
在Chisel里面是用一个自定义的类来定义模块的,这个类有以下三个特点:
- 继承自Module类。
- 有一个抽象字段“io”需要实现,该字段必须引用前面所说的端口对象。
- 在类的主构造器里进行内部电路连线。
因为非字段、非方法的内容都属于主构造方法,所以用操作符“:=”进行的赋值、用“<>”进行的连线或一些控制结构等等,都属于主构造方法。从Scala的层面来讲,这些代码在实例化时表示如何构造一个对象;从Chisel的层面来讲,它们就是在声明如何进行模块内部子电路的连接、信号的传递,类似于Verilog的assign和always语句。实际上这些用赋值表示的电路连接在转换成Verilog时,组合逻辑就是大量的assign语句,时序逻辑就是always语句。
还有一点需要注意,这样定义的模块会继承一个字段“clock”,类型是Clock,它表示全局时钟,在整个模块内都可见。对于组合逻辑,是用不上它的,而时序逻辑虽然需要这个时钟,但也不用显式声明。
还有一个继承的字段“reset”,类型是Reset,表示全局复位信号,在整个模块内可见。对于需要复位的时序元件,也可以不用显式使用该字段。
如果确实需要用到全局时钟和复位,则可以通过它们的字段名称来使用,但要注意类型是否匹配,经常需要“reset.toBool”这样的语句把Reset类型转换成Bool类型用于控制。隐式的全局时钟和复位端口只有在生成Verilog代码时才能看到。
要编写一个双输入多路选择器,其代码如下所示:
// mux2.scala
package testimport chisel3._class Mux2 extends Module {val io = IO(new Bundle{val sel = Input(UInt(1.W))val in0 = Input(UInt(1.W))val in1 = Input(UInt(1.W))val out = Output(UInt(1.W))})io.out := (io.sel & io.in1) | (~io.sel & io.in0)
}
在这里,“new Bundle { … }”的写法是声明一个匿名类继承自Bundle,然后实例化匿名类。对于短小、简单的端口列表,可以使用这种简便写法。对于大的公用接口,应该单独写成具名的Bundle子类,方便修改。“io.out := …”其实就是主构造方法的一部分,通过内建操作符和三个输入端口,实现了输出端口的逻辑行为。
2、例化模块
要例化一个模块,并不是直接用new生成一个实例对象就完成了,还需要再把实例的对象传递给单例对象Module的apply方法。这种别扭的语法是Scala的语法限制造成的,就像端口需要写成“IO(new Bundle {…})”,无符号数要写成“UInt(n.W)”等等一样。例如,下面的代码通过例化刚才的双输入多路选择器构建四输入多路选择器:
// mux4.scala
package testimport chisel3._
class Mux4 extends Module {val io = IO(new Bundle {val in0 = Input(UInt(1.W))val in1 = Input(UInt(1.W))val in2 = Input(UInt(1.W))val in3 = Input(UInt(1.W))val sel = Input(UInt(2.W))val out = Output(UInt(1.W))})val m0 = Module(new Mux2)m0.io.sel := io.sel(0)m0.io.in0 := io.in0m0.io.in1 := io.in1val m1 = Module(new Mux2)m1.io.sel := io.sel(0)m1.io.in0 := io.in2m1.io.in1 := io.in3val m2 = Module(new Mux2)m2.io.sel := io.sel(1)m2.io.in0 := m0.io.outm2.io.in1 := m1.io.outio.out := m2.io.out
}
3、例化多个模块
像上个例子中,模块Mux2例化了三次,实际只需要一次性例化三个模块就可以了。对于要多次例化的重复模块,可以利用向量的工厂方法VecInit[T <: Data]。因为该方法接收的参数类型是Data的子类,而模块的字段io正好是Bundle类型,并且实际的电路连线仅仅只需针对模块的端口,所以可以把待例化模块的io字段组成一个序列,或者按重复参数的方式作为参数传递。通常使用序列作为参数,这样更节省代码。生成序列的一种方法是调用单例对象Seq里的方法fill,该方法的一个重载版本有两个单参数列表,第一个接收Int类型的对象,表示序列的元素个数,第二个是传名参数,接收序列的元素。
因为Vec是一种可索引的序列,所以这种方式例化的多个模块类似于“模块数组”,用下标索引第n个模块。另外,因为Vec的元素已经是模块的端口字段io,所以要引用例化模块的某个具体端口时,路径里不用再出现“io”。例如:
// mux4_2.scala
package testimport chisel3._
class Mux4_2 extends Module {val io = IO(new Bundle {val in0 = Input(UInt(1.W))val in1 = Input(UInt(1.W))val in2 = Input(UInt(1.W))val in3 = Input(UInt(1.W))val sel = Input(UInt(2.W))val out = Output(UInt(1.W))})// 例化了三个Mux2,并且参数是端口字段ioval m = VecInit(Seq.fill(3)(Module(new Mux2).io)) m(0).sel := io.sel(0) // 模块的端口通过下标索引,并且路径里没有“io”m(0).in0 := io.in0m(0).in1 := io.in1m(1).sel := io.sel(0)m(1).in0 := io.in2m(1).in1 := io.in3m(2).sel := io.sel(1)m(2).in0 := m(0).outm(2).in1 := m(1).outio.out := m(2).out
}
4、动态命名模块
Chisel可以动态定义模块的名字,也就是转成Verilog时的模块名不使用定义的类名,而是使用重写的desiredName
方法的返回字符串。模块和黑盒都适用。例如:
class Coffee extends BlackBox {val io = IO(new Bundle {val I = Input(UInt(32.W))val O = Output(UInt(32.W))})override def desiredName = "Tea"
}class Salt extends Module {val io = IO(new Bundle {})val drink = Module(new Coffee)override def desiredName = "SodiumMonochloride"
}
对应的Verilog为:
module SodiumMonochloride(input clock,input reset
);wire [31:0] drink_O;wire [31:0] drink_I;Tea drink (.O(drink_O),.I(drink_I));assign drink_I = 32'h0;
endmodule
5、参数化的模块
chisel的模块本质其实就是scala的类,所以自然可以在定义时设置参数项,然后在使用时传参以生成不同的电路,如下所示:
class ParameterizedWidthAdder(in0Width: Int, in1Width: Int, sumWidth: Int) extends Module {require(in0Width >= 0)require(in1Width >= 0)require(sumWidth >= 0)val io = IO(new Bundle {val in0 = Input(UInt(in0Width.W))val in1 = Input(UInt(in1Width.W))val sum = Output(UInt(sumWidth.W))})// a +& b includes the carry, a + b does notio.sum := io.in0 +& io.in1
}
new ParameterizedWidthAdder(1, 4, 6)
6、MultiIOModule
和RawModule
介绍
我们在大多数情况下自定义的模块都是继承自Module
,但其实还有两种常用的module,分别是MultiIOModule
和RawModule
。
MultiIOModule
先说一下MultiIOModule
,它没有抽象字段io
需要实现,所以允许你根据需要只定义多个不同的IO,而不需要像前面介绍的那样必须实现抽象成员io
(但也允许采用和Module
一样的端口定义形式)。而且从MultiIOModule
生成的Verilog中的端口信号没有io_
前缀。MultiIOModule
也有隐式的时钟和复位信号。如下所示的例子:
// mux2.scala
import chisel3._class Mux2 extends MultiIOModule {val sel = IO(Input(UInt(1.W)))val in0 = IO(Input(UInt(1.W)))val in1 = IO(Input(UInt(1.W)))val out = IO(Output(UInt(1.W)))out := (sel & in1) | (sel & in0)
}
我的理解是:上述写法的优点在于可以很方便的在程序中增加一个或者多个端口,因为你现在只需要简单的定义一个任意变量名的变量即可,不再需要将端口放在
io
字段中,也不需要再使用Bundle
。
其实,
Module
也可以这样定义端口,但前提是已经把io
字段实现了,然后再采用该方式添加所需的端口。不过具体情况具体分析,只是语法上允许,具体要不要两种形式并存,以及并存定义了之后怎么用,视情况而定!
RawModule
RawModule
与MultiIOModule
相比在定义IO时是类似的,也可以采用上述格式。更重要的是,RawModule
没有隐式的时钟和复位信号,因此它多用于多时钟域设计中。