FPGA之道(45)正确的变量访问思路
文章目录
- 前言
- 正确的变量访问思路
- 变量访问思路概述
- 访问思路简述
- 写变量注意事项
- 读变量注意事项
- 赋值冲突
- 两个以上并行语句赋值冲突
- 两个以上串行语句赋值冲突
- 组合并行语句内串行语句的赋值冲突
- 时序并行语句内串行语句的赋值冲突
- 利用赋值冲突编写代码
- 总线是怎么回事?
- 总线的基本概念介绍
- 总线实现形式之选择器
- 原理简介
- 示例代码
- 总线实现形式之三态门
- 原理简介
- 赋值冲突?
- 示例代码
前言
对于变量的访问,遵循一定的规则,本文节选自《FPGA之道》,让我们一起站在巨人的肩膀上,来学习下这方面的经验。
正确的变量访问思路
变量访问思路概述
访问思路简述
变量的访问思路,简而言之就是“一写多读”,即如果有多个并行语句需要操作一个变量时,有且只能有一个固定的并行语句可以对变量进行写操作,而所有的并行语句都可以对变量进行读操作,因为一个变量只能有一个驱动源,如果被多个驱动源驱动,就会产生冲突。 这就好比在教室里上课一样,学生可以有很多,但是老师只能有一个,否则讲台上站着数、理、化、史、地、生一干老师,同时开讲,那学生们该听谁的呢?例如如下写变量的代码会造成变量访问冲突,从而导致编译出错:
-- VHDL example
process(a)
beginc <= a;
end
process(b)
beginc <= b; -- multi-driver
end// Verilog example
always@(a)c = a;
always@(b)c = b; // multi-driver
而如下的读变量代码则没有任何问题:
process(a)
beginc <= a;
end
process(a)
begind <= not a; -- OK
end// Verilog example
always@(a)c = a;
always@(a)d = ~a; // OK
写变量注意事项
关于“一写”,要把握一个原则,就是每次动作只能修改一次变量的值。动作可分为组合动作和时序动作,组合逻辑输入信号的每一次变化称为一个组合动作,而时钟信号的每一次有效沿称为一个时序动作。所谓“每次动作只能修改一次变量的值”,对于组合动作来说,由于输入唯一确定输出,因此组合动作发生后,输出只可能变化一次;对于时序动作来说,由于输出取决于时钟有效沿到来时刻的输入值,因此输出也只可能变化一次。在进行代码设计时,凡是与上述思路不符合的写变量操作都是无法实现的,例如下述的代码缔造者就想在一次动作发生后改变两次变量的值,但这是无法实现的:
-- VHDL example
process(a,b) -- 错误的组合动作思路
beginc <= a;c <= b;
end
process(clk) -- 错误的时序动作思路
beginif (clk'event and clk = '1') thend <= a;d <= b;end if;
end process;// Verilog example
always@(a,b) // 错误的组合动作思路
begin c = a;c = b;
end
always@(posedge clk) // 错误的时序动作思路
begin d <= a;d <= b;
end
上述代码并不会造成编译错误,事实上这属于赋值冲突问题,我们将在后面的【赋值冲突】小节进行详细介绍。
在实际中,由于路径延迟等的原因,每一次动作后,输出的确有可能发生多次反转,但这绝不是FPGA设计的初衷,而是由于组合逻辑中的“竞争与险象”。
读变量注意事项
关于“多读”,其实就是说该变量可以作为多个并行语句的输入变量,无论这些语句是描述时序逻辑还是组合逻辑。不过当读变量的并行语句同时也是那唯一的一条写变量并行语句时,一定要注意避免引入反馈,否则要不然不能综合,要不然不能仿真。例如如下的代码就存在着问题:
-- VHDL example process(a) -- 错误代码beginc <= a xor c;end process;// Verilog examplealways@(a) // 错误的代码begin c = a ^ c;end
但如下的代码就是完全没有问题的:
– VHDL example
process(clk) -- 正确的代码
beginif (clk'event and clk = '1') thenc <= a xor c;end if;
end process;// Verilog example
always@(posedge clk) // 正确的代码
begin c <= a ^ c;
end
这是因为时序逻辑是在时钟有效沿才将输入搬移到输出的,其反馈路径实际上是被触发器截断了的,因此确切的说上述时序逻辑代码中的不应该叫做反馈。
需要注意的是,数字电路中其实是可以存在反馈的,例如锁存器、触发器等时序单元就可以利用组合逻辑单元的正反馈实现,但这是更底层的细节,在FPGA中,HDL描述语言可以直接描述或调用锁存器、触发器等基本时序单元,而无需甚至无法创造它们。
赋值冲突
赋值冲突,是写变量时常碰到的一类问题,其主要可分为两类,分别介绍如下:
两个以上并行语句赋值冲突
这类赋值冲突对FPGA设计来说是致命的,因为它违背了变量操作中“一写”的原则。因此无论起冲突的并行语句情况是组合与组合、时序与时序、组合与时序,其都会造成编译器的报错,所以此类冲突必须禁止。
两个以上串行语句赋值冲突
这类冲突对编译器并不一定是致命的,但是由于其违背了“一写”中“每次动作只能修改一次变量的值”,因此当出现此类冲突时,FPGA设计的行为很可能与我们预期的不一致,从而造成问题。经过【本篇->编程思路->编写纯净的组合或时序逻辑】章节的学习,我们已经知道,为了避免不必要的麻烦,要尽量编写纯净的组合或时序逻辑,那么在本小节的讨论中,我们就主要针对纯净的组合逻辑并行语句和时序逻辑并行语句来进行赋值冲突讨论。
这里需要补充一点,在Verilog语言中,如果在一个always中对同一变量同时应用阻塞和非阻塞赋值,那么这类赋值冲突编译器是会报错的。
组合并行语句内串行语句的赋值冲突
这类赋值冲突可分为三种:
第一种,无反馈的组合串行赋值冲突。这类冲突发生时,按照HDL串行语句的执行思路,可知写在最后面的一条语句才是有效的。例如下述代码对应的都是一个逻辑或的功能:
-- VHDL example process(a, b)begint <= a and b; -- 无效赋值t <= a or b; -- 有效赋值end process;// Verilog examplealways@(a, b)
begint = a & b; //无效赋值t = a | b; //有效赋值
end
第二种:反馈在前的组合串行赋值冲突。这种赋值冲突的分析结果与上一种一致,尽管反馈现象是非常不好的,但是由于后续的无反馈代码对信号的值进行了重新设置,所以使得反馈代码相当于无效。例如下述代码对应的仍是一个逻辑或的功能:
-- VHDL example process(a, b)begint <= not t; -- 无效赋值t <= a or b; -- 有效赋值end process;// Verilog examplealways@(a, b)
begint = ~t; //无效赋值t = a | b; //有效赋值
end
第三种,反馈在后的组合串行赋值冲突。这类赋值冲突的分析结果与前两种截然不同,它不在是简单的忽略掉之前代码对变量的赋值操作,而是将之前代码得到的变量值代入到最后一个具有反馈的赋值语句中。例如,如下代码对应的是一个三输入与门的功能:
-- VHDL example process(a, b, c)begint <= a and b; t <= t and c; -- equal to : t <= a and b and c;end process;// Verilog examplealways@(a, b, c)
begint = a & b; t = t & c; //equal to : t = a & b & c;
end
时序并行语句内串行语句的赋值冲突
在【变量访问思路概述】小节中,我们已经分析过,时序逻辑中,实际上是不存在真正的反馈的,因此当位于描述时序逻辑的串行语句中出现赋值冲突时,按照HDL串行语句的执行思路,可知写在最后面的一条语句才是有效的。例如下述代码对应的都是一个逻辑或加一个触发器的功能:
-- VHDL example process(clk)beginif (clk’event and clk = ‘1’)thent <= a and b; -- 无效赋值t <= a or b; -- 有效赋值end if;end process;// Verilog examplealways@(posedge clk)
begint <= a & b; //无效赋值t <= a | b; //有效赋值
end
利用赋值冲突编写代码
一个规范且合理的HDL代码中,最好不要出现赋值冲突的情况,因为变量的驱动只能有一个。虽然出现了赋值冲突的代码仍然能够被编译,但总有一部分赋值冲突代码的存在是没有任何意义的,而且会令代码的阅读变得更加困难。不过赋值冲突现象也不是一无是处,有一种利用无反馈的组合串行赋值冲突来简化HDL代码的技巧,举例介绍如下。先看一段代码:
-- VHDL example process(sel)begincase (sel) is when "00" =>flaga <= 1'b1; flagb <= 1'b0;flagd <= 1'b0; flagd <= 1'b0;when "01" =>flaga <= 1'b0; flagb <= 1'b1;flagd <= 1'b0; flagd <= 1'b0;when "10" =>flaga <= 1'b0; flagb <= 1'b0;flagd <= 1'b1; flagd <= 1'b0;when "11" =>flaga <= 1'b0; flagb <= 1'b0;flagd <= 1'b0; flagd <= 1'b1;when others =>flaga <= 1'b0; flagb <= 1'b0;flagd <= 1'b0; flagd <= 1'b0;end case;end process;// Verilog examplealways@(sel)begincase (sel)2'b00 :
beginflaga = 1'b1; flagb = 1'b0;flagd = 1'b0; flagd = 1'b0;end2'b01 :
beginflaga = 1'b0; flagb = 1'b1;flagd = 1'b0; flagd = 1'b0;end2'b10 :
beginflaga = 1'b0; flagb = 1'b0;flagd = 1'b1; flagd = 1'b0;end2'b11 :
beginflaga = 1'b0; flagb = 1'b0;flagd = 1'b0; flagd = 1'b1;enddefault :
beginflaga = 1'b0; flagb = 1'b0;flagd = 1'b0; flagd = 1'b0;endendcase end
在上例中,由于描述的是一个组合逻辑电路,依据在【本篇->编程思路->编写纯净的组合或时序逻辑->组合逻辑描述方法】小节中的分析,必须在每一个条件分支中都保证flaga~flagd具有一个确定的输出值,否则将会在设计中引入锁存。因此上述代码会显得比较冗长。此时,只要稍稍利用一下无反馈的组合串行赋值冲突的分析结果,就可以在保证代码原有功能不变的前提下,极大的简化代码的书写,例如上例可以修改如下:
-- VHDL example
process(sel)
beginflaga <= 1'b0; flagb <= 1'b0;flagd <= 1'b0; flagd <= 1'b0;case (sel) is when "00" =>flaga <= 1'b1;when "01" => flagb <= 1'b1;when "10" =>flagd <= 1'b1; when "11" => flagd <= 1'b1;end case;
end process;// Verilog example
always@(sel)
beginflaga = 1'b0; flagb = 1'b0;flagd = 1'b0; flagd = 1'b0;case (sel)2'b00 : flaga = 1'b1;2'b01 : flagb = 1'b1;2'b10 : flagc = 1'b1; 2'b11 : flagd = 1'b1;endcase
end
对比上述两个例子,可以发现利用了无反馈的组合串行赋值冲突,对HDL代码的简化效果还是非常明显的。注意,以上例子是赋值冲突现象的一个正面应用案例,而在其他情况下,强烈建议大家一定要避免在代码中出现赋值冲突的情况。
总线是怎么回事?
关于“一写多读”的变量访问思路,有的人可能会质疑并举出反例——总线。你可能会觉得总线好像就是一个真正可以“多写多读”的对象,其实不然!在这一小节中,我们将详细介绍一下总线的概念及其实现方式,大家也可以利用与总线类似的思路,来完成一些“多写多读”的事情。
总线的基本概念介绍
总线,英文为Bus,是系统各种功能部件之间传送信息的公共通信干线。例如,SPI、C、PCI、PCIe、PXI、PXIe、USB、SATA、1553B、CAN等等,都是一些常用的总线规范。
假设系统现在有5个部件——A、B、C、D、E,互相之间都需要传递数据,如果没有总线,那么它们之间的连接关系很可能如下图所示。如果此时系统中又加入一个部件F,那么系统新增的部分如图中虚线所示:
(注意,为了能够互相传递数据,上图中的每根连线实际对应输入、输出两根连线)
如果在系统中引入总线结构,那么5个部件之间的连接关系很可能如下图所示。如果此时系统中又加入一个部件F,那么系统新增的部分如图中虚线所示:
对比上述两种情况,我们可以明显感受到总线为系统设计带来的好处,即:简化了系统的结构和硬件设计;便利了系统的扩充和更新;简化了系统的调试和维修。
当然了,总线也有一定的缺点,那就是系统中的各个部件只能分时使用总线,而无法同时使用。因此从微观上来说,在某一固定时刻,总线都是“一写多读”的,只不过通过一些控制电路,我们貌似可以在不同时刻为总线分配不同的驱动源,以达到宏观上的“多写多读”。由于总线是被大家分时使用的,那么对于一些实时性要求很高的通信要求,就比较难以胜任。除此以外,必须引入额外的控制和筛选机制,以保证连接到总线的各个部件都知道什么时候可以写总线以及什么时候总线上的数据是发给自己的。
了解了总线的优缺点之后,接下来就让我们来看一下如何在FPGA中实现总线吧。
总线实现形式之选择器
利用多路选择器的选通特性,可以实现总线的功能,这也是在FPGA芯片内部设计总线时最常用的方法,因为FPGA芯片内部有着非常丰富的MUX资源。
原理简介
选择器式总线的原理图如下:
从图中我们可以看出,总线仍然是“一写多读”的,其驱动源只有一个,那就是MUX的输出。而由于MUX具有选通功能,所以可以通过改变选通信号的值来改变写入总线数据的来源,因此给我们以一种“多写多读”的错觉。
示例代码
本小节为大家提供了一个非常简单的选择器式总线的FPGA实现方式。功能代码主要分为三个部分:
1、选择器控制信号产生部分,采用抢占式优先级译码器的思路,根据四个外围器件的请求信号产生多路选择器的选择控制信号。
2、写总线部分,即将选通通道的数据写到总线上。
3、读总线部分,各个外围器件根据自己的情况将总线数据读入器件内部进行运算和处理。
示例代码如下:
-- VHDL example
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;entity SimplestMuxBus is
port (request0, request1, request2, request3 : in std_logic;device0Out, device1Out, device2Out, device3Out : in std_logic_vector(7 downto 0);device0In, device1In, device2In, device3In : out std_logic_vector(7 downto 0)
);
end SimplestMuxBus;architecture Behavioral of SimplestMuxBus issignal requestAll : std_logic_vector(3 downto 0);signal sel : std_logic_vector(1 downto 0);signal sysBus : std_logic_vector(7 downto 0);begin-- generate sel
requestAll <= request0 & request1 & request2 & request3;
process(requestAll)
begincase (requestAll) is when "0001" =>sel <= "11";when "0010"|"0011" =>sel <= "10";when "0100"|"0101"|"0110"|"0111" => sel <= "01";when others => sel <= "00";end case;
end process;-- write sysBus
process(sel, device0Out, device1Out, device2Out, device3Out)
begincase (sel) is when "00" =>sysBus <= device0Out;when "01" =>sysBus <= device1Out;when "10" => sysBus <= device2Out;when "11" => sysBus <= device3Out;when others => sysBus <= device0Out;end case;
end process;-- read sysBus
device0In <= sysBus;
device1In <= sysBus;
device2In <= sysBus;
device3In <= sysBus; end Behavioral;// Verilog example
module SimplestMuxBus (
input request0, request1, request2, request3,
input [7:0] device0Out, device1Out, device2Out, device3Out,
output [7:0] device0In, device1In, device2In, device3In
);reg [1:0] sel;
reg [7:0] bus;//generate sel
always@(request0, request1, request2, request3)
begin//Priority decodercase ({request0, request1, request2, request3})4'b0001 : sel = 2'b11;4'b0010 - 4'b0011 : sel = 2'b10;4'b0100 - 4'b0111 : sel = 2'b01;default : sel = 2'b00;endcase
end//write bus
always@(sel, device0Out, device1Out, device2Out, device3Out)
begin case (sel)2'b00 : bus = device0Out;2'b01 : bus = device1Out;2'b10 : bus = device2Out;2'b11 : bus = device3Out;default : bus = device0Out;endcase
end//read bus
assign device0In = bus;
assign device1In = bus;
assign device2In = bus;
assign device3In = bus;endmodule
总线实现形式之三态门
利用三态门的高阻特性,可以实现总线的功能,这也是硬件电路系统中设计总线时最常用的方法。由于三态门资源大多存在于FPGA芯片的接口资源中而非FPGA芯片内部,因此一般当总线位于FPGA芯片的外部时,常采用这种方式来实现。
原理简介
三态门式总线的原理图如下:
从图中我们可以看出,总线正常工作时,最多只能有一个三态门被选通,否则总线上将会发生数据冲突,从而导致系统运转不正常。因此三态门式总线仍然是“一写多读”的,并且其驱动源只有一个,那就是被选通的那个三态门的输出。也许你会说,此时总线的驱动源就不是固定的了,因为每次被选通的三态门可以不同。不过这里请大家注意,三态门的高阻实际上就相当于电路的断路,因此选通不同的三态门,其实相当于对数字电路从结构上做了动态的调整与修改,因此改变选通情况的前后两种状态实际上是对应两种不同的数字电路,而要求驱动源固定是针对同一个数字电路来说的,因此此类总线结构并不和我们之前所介绍的概念冲突。而由于三态门的高阻特性,可以通过改变选通信号的值来动态改变各个输入源于总线之间的电路连接关系,因此带给我们一种“多写多读”的错觉。
赋值冲突?
经过【赋值冲突】章节的学习,我们知道如下代码的写法是错误的,也是无法通过编译的。
-- VHDL example
c <= a when sel(0) = '1' else '0';
c <= b when sel(1) = '1' else '1'; // Verilog example
assign c = (sel[0] == 1'b1) ? a : 1'b0;
assign c = (sel[1] == 1'b1) ? b : 1'b1;
但是,以下的写法却是正确的,且不会产生赋值冲突。
-- VHDL example
c <= a when sel(0) = '1' else 'Z';
c <= b when sel(1) = '1' else 'Z'; // Verilog example
assign c = (sel[0] == 1'b1) ? a : 1'bz;
assign c = (sel[1] == 1'b1) ? b : 1'bz;
对比以上两种写法,可以发现唯一的区别就是高阻态的使用。为什么使用了高阻态,代码就是正确的呢?这是因为高阻态实际上就是电路中的断路,通过合理的切换到高阻态,可以让上述有高阻态例子中的两条赋值语句不会在同一个时刻生效,因此不会产生赋值冲突。而没有高阻态的例子代码,两条赋值语句肯定都是一直生效的,因此必然会产生赋值冲突从而导致出错。
示例代码
本小节为大家提供了一个非常简单的三态门式总线的FPGA实现方式。功能代码主要分为三个部分:
1、三态门控制信号产生部分,采用抢占式优先级译码器的思路,根据四个外围器件的请求信号产生三态门的选通控制信号。
2、写总线部分,即将四个三态门的输出连接到总线上。
3、读总线部分,各个外围器件根据自己的情况将总线数据读入器件内部进行运算和处理。
示例代码如下:
-- VHDL example library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_ARITH.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;entity simplestZBus isport (request0, request1, request2, request3 : in std_logic;device0Out, device1Out, device2Out, device3Out : in std_logic_vector(7 downto 0);device0In, device1In, device2In, device3In : out std_logic_vector(7 downto 0));end simplestZBus;architecture Behavioral of simplestZBus issignal requestAll : std_logic_vector(3 downto 0);signal en : std_logic_vector(3 downto 0);signal sysBus : std_logic_vector(7 downto 0);begin-- generate selrequestAll <= request0 & request1 & request2 & request3;process(requestAll)begincase (requestAll) is when "0000" =>en <= "0000";when "0001" =>en <= "1000";when "0010"|"0011" =>en <= "0100";when "0100"|"0101"|"0110"|"0111" => en <= "0010";when others => en <= "0001";end case;end process;-- write sysBussysBus <= device0Out when en(0) = '1' else "ZZZZZZZZ";sysBus <= device1Out when en(1) = '1' else "ZZZZZZZZ";sysBus <= device2Out when en(2) = '1' else "ZZZZZZZZ";sysBus <= device3Out when en(3) = '1' else "ZZZZZZZZ";-- read sysBusdevice0In <= sysBus;device1In <= sysBus;device2In <= sysBus;device3In <= sysBus; end Behavioral;// Verilog examplemodule simplestZBus(input request0, request1, request2, request3,input [7:0] device0Out, device1Out, device2Out, device3Out,output [7:0] device0In, device1In, device2In, device3In);reg [3:0] en;wire [7:0] bus;//generate selalways@(request0, request1, request2, request3)begin//Priority decodercase ({request0, request1, request2, request3})4'b0000 : en = 4'b0000;4'b0001 : en = 4'b1000;4'b0010, 4'b0011 : en = 4'b0100;4'b0100,4'b0101,4'b0110, 4'b0111 : en = 4'b0010;default : en = 4'b0001;endcaseend//write busassign bus = (en[0] == 1'b1) ? device0Out : 8'hzz;assign bus = (en[1] == 1'b1) ? device1Out : 8'hzz;assign bus = (en[2] == 1'b1) ? device2Out : 8'hzz;assign bus = (en[3] == 1'b1) ? device3Out : 8'hzz;//read busassign device0In = bus;assign device1In = bus;assign device2In = bus;assign device3In = bus;endmodule
FPGA之道(45)正确的变量访问思路相关推荐
- 【 Verilog HDL 】正确的变量访问思路
以前对这个话题也写了至少两次了,很多人在编写HDL程序时候,也时常遇到这个问题,那就是多驱动问题,今天终于看到了规范的说法了. Modelsim下进行功能仿真没问题,可是在ISE综合报错,如何解决? ...
- FPGA之道(33)Verilog数据类型
文章目录 前言 Verilog数据类型 Verilog四值逻辑系统 寄存器数据类型 reg integer real 线网数据类型 wire tri supply1/supply0 wand/tria ...
- FPGA之道(31)VHDL编写注意事项
文章目录 前言 VHDL编写注意事项 大小写不敏感 VHDL中的关键字 多余的符号 纠结的downto 与to 数组范围混用 逻辑向量范围混用 范围中的变量 仿真雷区 进程敏感量表缺失 进程间语句顺序 ...
- FPGA之道(24)VHDL数据类型
文章目录 前言 VHDL数据类型 常用数据类型 逻辑数据类型 std_logic std_logic_vector boolean bit bit_vector 数值数据类型 integer real ...
- FPGA之道(51)数据的存储
文章目录 前言 数据的存储 为什么需要数据存储 数据存储的载体 FPGA芯片内部的载体 触发器 查找表 块存储 FPGA芯片外部的资源 数据存储的形式.实现及应用场合 寄存器 特征简介 实现载体 应用 ...
- FPGA之道(84)功能仿真之Verilog Test Fixture
文章目录 前言 Verilog Test Fixture "Hello world"之Verilog Test Fixture 待仿真设计 仿真示例 示例详解 仿真结果 继承描述语 ...
- FPGA之道(82)功能仿真之仿真原理
文章目录 前言 功能仿真篇 仿真原理 串行模仿并行思路分析 有限模仿无限思路分析 组合逻辑仿真原理 时序逻辑仿真原理 HDL的仿真原理 仿真时间与物理时间 前言 本文摘自<FPGA之道>. ...
- FPGA之道(79)静态时序分析(五)外部接口的相关时序分析
文章目录 前言 外部接口相关时序分析 功能仿真对接口分析的帮助 纯输入接口 纯时钟输入接口 纯同步输入接口 纯异步输入接口 同步输入异步采集 纯输出接口 纯时钟输出接口 纯同步输出接口 纯异步输出接口 ...
- FPGA之道(69)提高设计的综合性能(一)提高设计的鲁棒性
文章目录 前言 提高设计的鲁棒性 一些影响设计正常工作的原因 非法输入 环境干扰 应对方法之输入预处理 应对方法之RAM替换FIFO 应对方法之状态机超时跳转 应对方法之三模冗余 应对方法之全局复位 ...
最新文章
- 比 Spring Boot 快 10 倍的 Bootique 框架
- 软件工程(2019)第三次个人作业
- C++实现字符串数组作为函数的参数的反序输出
- linux proc 占用空间,一种诡异的Linux磁盘空间被占满问题
- vue import组件的使用
- python怎么安装beautifulsoup,python – 安装BeautifulSoup
- 【Docker】Docker容器和主机如何互相拷贝传输文件
- python列表生成式原理_三元表达式/和/或如何在Python中工作/真与假的性质/列表生成/生成器,and,or,执行,原理,True,False,本质,生成式...
- mysql 1054 42s22_MySQL 触发器的坑:ERROR 1054 (42S22): Unknown column 'xxx' in 'field list'
- 浅谈css常用伪类用法
- c#通过网络链接打印PDF
- Redis 缓存清理策略
- 美国电子烟走向规范化,“下一站”是中国?
- HLS 开发学习(五) 稀疏矩阵向量乘法
- UVA 1471 Defense Lines 防线
- Swagger2 WARN:Coercing to be of type string. This may not even be a scalar type in actua
- Java新人之路 -- 集合(上)
- 正常性入职岗前培训(非培训机构的实习生入职培训)---基础知识总结
- iOS 把数据库文件打包到mainbundle中,查找不到路径的解决的方法;以及在删除bundle中文件的可行性...
- JavaWeb基础入门到上手项目
热门文章
- 这种事都有?建行网银把Demo版的放上线了?!
- if函数中的android,java - 我需要帮助将IF语句方法更改为Android Studio中的SWITCH情况 - 堆栈内存溢出...
- 基于c语言开发老人防摔系统,家有老人,做好防摔排查
- suse linux 软件包安装,SUSE Linux 11系统rpm包离线安装GCC
- java nio channel原理_Java NIO 选择器(Selector)与通道(Channel) 原理 | 学步园
- linux的dns查询工具,一款超强的统计DNS查询的工具--DNSTOP
- mysql 8.0什么时候发布_MySQL 8.0.22正式发布
- java怎么将前端的数据存到关联的表中_MySQL数据库性能优化
- 华中科技大学计算机组成计算机数据表示实验(hust)平台_华中科技大学各院系2020年博士研究生“申请考核”制拟录取名单公示...
- php多流程,多种php开发环境搭建流程