猜数字游戏
还记得第一次课上我们演示的猜数字小游戏吗?在本方向中,我们将带大家自顶向下地实现这个项目。
猜数字小游戏的基本过程如下:首先,程序随机生成 3 个介于 0 ~ 5 之间的、互不相同的数码,由玩家进行猜测。玩家每次通过拨码开关(Switches)输入自己猜测的数据序列,按下按钮表明输入完毕。随后,程序检查玩家最近的三次输入和生成的数码是否匹配,并通过 LED 给出对应的反馈结果。玩家在看到结果之后,会再次按下按钮以继续输入自己的猜测。在玩家操作同时,数码管(Segments)会显示一个倒计时,在倒计时结束之前如果匹配正确则视为用户胜利,否则会视为用户失败。之后,用户再次按下按钮,以开始一局新的游戏。
编码开关的输入方式为:
sw[0] - 输入 0 sw[1] - 输入 1 sw[2] - 输入 2 sw[3] - 输入 3 sw[4] - 输入 4 sw[5] - 输入 5
开关的输入为上升沿有效,在其被拨上去时视作输入了一个对应的数字,将开关从开启状态拨动到关闭状态不会触发任何输入动作。按下按钮后,我们仅将最近的三次输入作为用户的最终输入,例如:假定当前的开关状态为 sw = 8'B0011_0011,经过下面的一系列操作:
sw = 8'B0011_0011 // 初始状态 sw = 8'B0010_0011 // 拨下 sw[4] sw = 8'B0010_1011 // 拨上 sw[3] sw = 8'B0010_1111 // 拨上 sw[2] sw = 8'B0010_1110 // 拨下 sw[0] sw = 8'B0011_1110 // 拨上 sw[5] sw = 8'B0011_1111 // 拨上 sw[0]
随后按下按钮,则此时程序接收到的输入序列为 12'H250。那么如果用户输入了限制范围之外的数值,例如拨动了 sw[7] 应当怎么处理呢?这个问题我们可以之后再考虑。
按钮的作用这里我们让按钮作为用户输入完成的确定信号。因此,在用户通过开关输入数据时,后续的匹配模块并不会接收到数据;仅当用户按下按钮后,匹配模块才会接收到用户的最近三次输入信息。
匹配阶段需要比较用户猜测的结果和目标结果之间的相似程度。考虑到我们只有三位数,所有可能的结果可以拆成下面几种情况:
一个数字都没对; 只对了一个数,且位置不正确; 只对了一个数,但位置正确; 对了两个数,但位置都不正确; 对了两个数,但一个位置正确,一个不正确; 对了两个数,且位置都正确; 对了三个数,且位置都不正确; 对了三个数,但一个位置正确,两个不正确; 对了三个数,且位置都正确。我们可以使用 led[5:0] 表示上面的九种情况。对应的关系如下:
led[2:0]:用于指示当前正确但位置不正确的数字数目。其中
led[2] 亮起代表 3 个数都正确但位置均不正确 led[1] 亮起代表有 2 个数正确但位置均不正确 led[0] 亮起代表有 1 个数正确但位置不正确 如果均不亮起代表没有正确的数字led[5:3]:用于指示当前正确且位置正确的数字数目。其中
led[5] 亮起代表 3 个数位置都正确 led[4] 亮起代表有 2 个数位置正确 led[3] 亮起代表有 1 个数位置正确 如果均不亮起代表没有位置正确的数字。例如,如果用户输入为 023,而当前游戏的答案为 520,则 LED 灯的结果为 led[5:0] = 6'b001_001,即对了两个数,但一个位置正确,一个不正确。如果用户输入为 052,则 LED 灯的结果为 led[5:0] = 6'b000_100,即 3 个数都正确但位置均不正确。九种情况的完整对应关系如下:
一个数字都没对:led[5:0] = 6'b000_000 只对了一个数,且位置不正确:led[5:0] = 6'b000_001 只对了一个数,但位置正确:led[5:0] = 6'b001_000 对了两个数,但位置都不正确:led[5:0] = 6'b000_010 对了两个数,但一个位置正确,一个不正确:led[5:0] = 6'b001_001 对了两个数,且位置都正确:led[5:0] = 6'b010_000 对了三个数,且位置都不正确:led[5:0] = 6'b000_100 对了三个数,但一个位置正确,两个不正确:led[5:0] = 6'b001_010 对了三个数,且位置都正确:led[5:0] = 6'b100_000(这种情况会一闪而过,因为游戏已经胜利了,LED 会全部亮起)当游戏胜利时,LED 灯会全部亮起,数码管显示 32'H88888888;当游戏失败时,LED 灯会全部熄灭,数码管显示 32'H44444444。为了增加游戏的动态效果,我们让 LED 在用户输入的过程中(还没有按下按钮表示输入完成)呈现 Lab3 所示的流水灯效果。
提示游戏的流程如下图所示:
2.2.2 系统设计
根据以上的内容,我们可以设计如下图所示的电路结构。
整个项目可以分为如下的几个部分:
外设输入。包括:开关输入处理模块、串口输入处理模块等; 外设输出。包括:数码管显示模块、串口输出处理模块等; 计时器。包括:核心计时器、计时复位控制模块等; 数据检查模块。包括:随机数生成器、结果检测模块等; 中央控制器。我们将分为基础版和进阶版两部分进行说明。
★ 2.2.3 基础内容
为了实现最为基础的功能,Basic 版本的猜数字小游戏硬件结构如下图所示:
其中,rst 信号由 sw[7] 引出,作为全局复位信号。当 rst 信号为高电平时,系统内所有的寄存器都会被复位。LED_flow 模块为 Lab3 中编写的流水灯模块。
2.2.3.1 开关输入
首先,我们需要确定用户拨动开关的时机以及对应的编号,这就是 SW_Input 模块的作用。模块的端口约定如下:
SW_Input1 2 3 4 5 6 7 8
module Input( input [ 0 : 0] clk, input [ 0 : 0] rst, input [ 7 : 0] sw, output reg [ 3 : 0] hex, output [ 0 : 0] pulse );
其中 sw 是来自开发板的开关输入,hex 信号用于指示当前拨动的开关编号,范围是 4'd0 至 4'd7。之所以使用 4bits 位宽是为了便于之后的扩展。pulse 信号为时钟同步的高电平脉冲,当某开关由关闭变为开启状态时,pulse 会发出一个时钟周期的高电平信号。
模块的代码框架如下:
SW_Input1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
module Input( input [ 0 : 0] clk, input [ 0 : 0] rst, input [ 7 : 0] sw, output reg [ 3 : 0] hex, output [ 0 : 0] pulse ); // 三级寄存器边沿检测 reg [7:0] sw_reg_1, sw_reg_2, sw_reg_3; always @(posedge clk) begin if (rst) begin sw_reg_1 <= 0; sw_reg_2 <= 0; sw_reg_3 <= 0; end else begin // TODO:补充边沿检测的代码 end end wire [7:0] sw_change = // TODO:检测上升沿 // TODO:编写代码,产生 hex 和 pulse 信号。 // Hint:这两个信号均为组合逻辑产生。 endmodule
下面是对应的示例波形图(图中存在误差,实际的信号应均为时钟同步的。所有的数据均为十六进制):
完成 SW_Input 模块后,我们需要引入一个移位寄存器,用于存储用户最近的开关输入情况。
Shift_Reg1 2 3 4 5 6 7 8 9 10 11 12 13 14
module ShiftReg( input [ 0 : 0] clk, input [ 0 : 0] rst, input [ 3 : 0] hex, input [ 0 : 0] pulse, output reg [31 : 0] dout ); always @(posedge clk) begin if (rst) dout <= 0; else if (pulse) dout <= {{dout[27: 0]}, {hex}}; end endmodule
这样就实现了用户输入的保存。
题目 2-A-1
请根据以上内容,按要求完成本项练习。
2.2.3.2 数据检查模块
数据检查模块 Check 用于确定当前用户的输入是否与游戏目标相符,并给出对应的检测结果。模块的端口定义如下:
Check.v1 2 3 4 5 6 7 8 9 10
module Check( input [ 0 : 0] clk, input [ 0 : 0] rst, input [11 : 0] input_number, input [11 : 0] target_number, input [ 0 : 0] start_check, output [ 5 : 0] check_result );
其中,input_number 为来自用户的输入,target_number 为本局游戏的目标,start_check 信号来自控制单元。当 start_check 信号为高电平时,模块会给出对应的 check_result。在这里,check_result 就是上面提到的 LED 信号。例如:check_result = 6'B001_001 代表有两个数字正确,其中一个数字位置正确,另一个位置不正确。
Check 模块的代码框架如下:
Check.v1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
module Check( input [ 0 : 0] clk, input [ 0 : 0] rst, input [11 : 0] input_number, input [11 : 0] target_number, input [ 0 : 0] start_check, output [ 5 : 0] check_result ); // 模块内部用寄存器暂存输入信号,从而避免外部信号突变带来的影响 reg [11:0] current_input_data, current_target_data; always @(posedge clk) begin if (rst) begin current_input_data <= 0; current_target_data <= 0; end else if (start_check) begin current_input_data <= input_number; current_target_data <= target_number; end end // 使用组合逻辑产生比较结果 wire [3:0] target_number_3, target_number_2, target_number_1; wire [3:0] input_number_3, input_number_2, input_number_1; assign input_number_1 = current_input_data[3:0]; assign input_number_2 = current_input_data[7:4]; assign input_number_3 = current_input_data[11:8]; assign target_number_1 = current_target_data[3:0]; assign target_number_2 = current_target_data[7:4]; assign target_number_3 = current_target_data[11:8]; reg i1t1, i1t2, i1t3, i2t1, i2t2, i2t3, i3t1, i3t2, i3t3; always @(*) begin i1t1 = (input_number_1 == target_number_1); i1t2 = (input_number_1 == target_number_2); i1t3 = (input_number_1 == target_number_3); i2t1 = (input_number_2 == target_number_1); i2t2 = (input_number_2 == target_number_2); i2t3 = (input_number_2 == target_number_3); i3t1 = (input_number_3 == target_number_1); i3t2 = (input_number_3 == target_number_2); i3t3 = (input_number_3 == target_number_3); end // TODO:按照游戏规则,补充 check_result 信号的产生逻辑 endmodule Tips
实际上,Check 模块可以被设计成一个纯粹的组合逻辑模块,即对于任何给定的 target_number 和 input_number,都能异步地给出 check_result 信号。你可以自行选择自己喜欢的实现方式。
提示Basic 版本的猜数字游戏无法随机生成题目,因此在例化 Check 模块时,可以先暂时将 target_number 设定成一个固定常数。
题目 2-A-2
请根据以上内容,按要求完成本项练习。
2.2.3.3 计时器
计时器模块 Timer 用于控制倒计时的开启与关闭。模块的端口定义如下:
Timer.v1 2 3 4 5 6 7 8 9 10 11 12 13
module Timer( input [ 0 : 0] clk, input [ 0 : 0] rst, input [ 0 : 0] set, input [ 0 : 0] en, output [ 7 : 0] minute, output [ 7 : 0] second, output [11 : 0] micro_second, output [ 0 : 0] finish );
其中,set 信号用于设置计时器的起始值。当 set 信号有效时,内部的计时器会被设置为 01 分 00 秒 000 毫秒。en 信号来自控制单元。当 en 信号有效时,计时器会持续倒计时;当 en 信号为低电平时,计时器会暂停倒计时(而不是清零或复位)。finish 信号在内部计时器值为 0 分 0 秒 000 毫秒时为高电平,用于告知控制单元此时已经计时完毕。minute、second 和 micro_second 分别对应当前计时器的分、秒、微秒数值。
提醒上述所有的数值均为二进制表示,而不是 BCD 码表示。也就是说,minute 的上限值是 8'D59 或 8'H37,而不是 8'B0101_1001。
计时器内部的核心为串行计时单元和一个状态机。串行计时单元基于如下的 Clock 模块:
Clock.v1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
module Clock # ( parameter WIDTH = 32, parameter MIN_VALUE = 0, parameter MAX_VALUE = 999, parameter SET_VALUE = 500 ) ( input [ 0 : 0] clk, input [ 0 : 0] rst, input [ 0 : 0] set, input [ 0 : 0] carry_in, output reg [ 0 : 0] carry_out, output reg [WIDTH-1: 0] value ); always @(posedge clk) begin if (rst) value <= 0; else if (set) value <= SET_VALUE; else if (carry_in) begin if (value <= MIN_VALUE) value <= MAX_VALUE; else value <= value - 1; end end always @(*) carry_out = (carry_in) && (value == MIN_VALUE); endmodule
Clock 模块是串行计数器的基本模块。其中 carry_in 是来自低位的进位信号,carry_out 是向高位的进位信号。Clock 模块仅在 carry_in 有效时倒计时一次,仅在倒计时达到下限 MIN_VALUE 时发出一次 carry_out 信号。下面是计时器的基本结构:
毫秒计数器每 1ms 跳动一次,当减少到 0 时触发秒计数器跳动一次;秒计数器为 0 时触发分计数器跳动一次,依次类推,这样就实现了串行计数。
计时器内部的状态机则比较简单,仅包含两个状态。
计时器在计时状态下才会启动毫秒计时器,用于产生 micro_second_clock 的 carry_in 信号;在停止状态下,该信号将保持为 0。
Timer 模块的代码框架如下:
Timer.v1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
module Timer( input [ 0 : 0] clk, input [ 0 : 0] rst, input [ 0 : 0] set, input [ 0 : 0] en, output [ 7 : 0] minute, output [ 7 : 0] second, output [11 : 0] micro_second, output [ 0 : 0] finish ); reg current_state, next_state; localparam ON = 1; localparam OFF = 0; always @(posedge clk) begin if (rst) current_state <= OFF; else current_state <= next_state; end // TODO: Finish the FSM localparam TIME_1MS = 100_000_000 / 1000; reg [31 : 0] counter_1ms; // TODO: Finish the counter wire carry_in[2:0]; Clock # ( .WIDTH (8) , .MIN_VALUE (0) , .MAX_VALUE (59) , .SET_VALUE (1) ) minute_clock ( .clk (clk), .rst (rst), .set (set), .carry_in (carry_in[2]), .carry_out (finish), .value (minute) ); Clock # ( .WIDTH (8) , .MIN_VALUE (0) , .MAX_VALUE (59) , .SET_VALUE (0) ) second_clock ( .clk (clk), .rst (rst), .set (set), .carry_in (carry_in[1]), .carry_out (carry_in[2]), .value (second) ); Clock # ( .WIDTH (12) , .MIN_VALUE (0) , .MAX_VALUE (999) , .SET_VALUE (0) ) micro_second_clock ( .clk (clk), .rst (rst), .set (set), .carry_in (carry_in[0]), .carry_out (carry_in[1]), .value (micro_second) ); // TODO: what's carry_in[0] ? endmodule
题目 2-A-3
请根据以上内容,按要求完成本项练习。
2.2.3.4 核心控制器
Control 模块用于控制整个电路的正常工作。其端口约定如下:
Control.v1 2 3 4 5 6 7 8 9 10 11 12 13 14
module Control ( input [ 0 : 0] clk, input [ 0 : 0] rst, input [ 0 : 0] btn, input [ 5 : 0] check_result, output reg [ 0 : 0] check_start, output reg [ 0 : 0] timer_en, output reg [ 0 : 0] timer_set, input [ 0 : 0] timer_finish, output reg [ 1 : 0] led_sel, output reg [ 1 : 0] seg_sel );
你需要结合数据通路与各个子模块的定义,自行设计 Control 模块中的状态机,并确定各个信号的产生逻辑。
题目 2-A-4
请根据以上内容,按要求完成本项练习。
至此,我们已经实现了 Basic 版本的大部分内容。
题目 2-A-5
请根据以上内容,按要求完成本项练习。
2.2.4 进阶内容
进阶内容是在 Basic 版本的基础上进行的细节添加。
2.2.4.1 BCD 显示
你可能已经注意到了,目前数码管的倒计时是十六进制的格式。这是由于我们从 Timer 模块直接将结果 current_time 连到了数码管的输出上,而 Segment 模块是直接将 output_data 的某几位作为 seg_data 的输出的。
现在,我们需要让数码管的倒计时显示为 8421BCD 编码的格式。为此我们需要在 Timer 模块和 Segment 模块之间添加一个 Hex2BCD 模块。该模块可以将输入的十六进制(其实也就是二进制)数据转换为对应的十进制 BCD 码。例如:
8'H0A -> 10 -> 8'B0001_0000 8'H1C -> 28 -> 8'B0010_1000 8'H20 -> 32 -> 8'B0011_0010
关于如何进行编码转换,你可以参考这篇文章与这篇文章的相关内容进行设计。
Tips本题允许使用循环语句。但如果你使用了循环语句,那么需要在检查时向助教介绍这段代码实际对应的电路结构。
2.2.4.2 闪烁的倒计时
在 Lab3 的选择性必做题目 3 里,我们设计了一个带有掩码功能的 Segment 模块。现在,我们希望基于该模块添加倒计时闪烁的功能。描述如下:
剩余时间大于 10s:不闪烁 剩余时间大于 3s 且不超过 10s:以 1Hz 的频率闪烁(数码管亮起 0.5s,熄灭 0.5s) 剩余时间不超过 3s:以 2Hz 的频率闪烁(数码管亮起 0.25s,熄灭 0.25s) 胜利或失败界面:不闪烁请自行设计模块并修改数据通路,实现上述的功能。
2.2.4.3 随机数生成器
现在的猜数字游戏只有一道题目,因此只能进行一局游戏。为了支持更为多元的游戏模式,我们希望每一局游戏的答案都能「随机」产生。这就是随机数生成器的作用了。
首先,我们需要明确题目可能的数目。对于六个数码组成的无重复数值,总共有 种不同的结果。我们使用一个 reg 型数组 targets 进行存储:
reg [11:0] targets [0: 127]; always @(posedge clk) begin // 这里用 initial 也可以,只要让 targets 中的数值保持不变即可。 targets[0] <= 12'h012; targets[1] <= 12'h013; targets[2] <= 12'h014; targets[3] <= 12'h015; targets[4] <= 12'h021; targets[5] <= 12'h023; targets[6] <= 12'h024; targets[7] <= 12'h025; targets[8] <= 12'h031; targets[9] <= 12'h032; targets[10] <= 12'h034; targets[11] <= 12'h035; targets[12] <= 12'h041; targets[13] <= 12'h042; targets[14] <= 12'h043; targets[15] <= 12'h045; targets[16] <= 12'h051; targets[17] <= 12'h052; targets[18] <= 12'h053; targets[19] <= 12'h054; targets[20] <= 12'h102; targets[21] <= 12'h103; targets[22] <= 12'h104; targets[23] <= 12'h105; targets[24] <= 12'h120; targets[25] <= 12'h123; targets[26] <= 12'h124; targets[27] <= 12'h125; targets[28] <= 12'h130; targets[29] <= 12'h132; targets[30] <= 12'h134; targets[31] <= 12'h135; targets[32] <= 12'h140; targets[33] <= 12'h142; targets[34] <= 12'h143; targets[35] <= 12'h145; targets[36] <= 12'h150; targets[37] <= 12'h152; targets[38] <= 12'h153; targets[39] <= 12'h154; targets[40] <= 12'h201; targets[41] <= 12'h203; targets[42] <= 12'h204; targets[43] <= 12'h205; targets[44] <= 12'h210; targets[45] <= 12'h213; targets[46] <= 12'h214; targets[47] <= 12'h215; targets[48] <= 12'h230; targets[49] <= 12'h231; targets[50] <= 12'h234; targets[51] <= 12'h235; targets[52] <= 12'h240; targets[53] <= 12'h241; targets[54] <= 12'h243; targets[55] <= 12'h245; targets[56] <= 12'h250; targets[57] <= 12'h251; targets[58] <= 12'h253; targets[59] <= 12'h254; targets[60] <= 12'h301; targets[61] <= 12'h302; targets[62] <= 12'h304; targets[63] <= 12'h305; targets[64] <= 12'h310; targets[65] <= 12'h312; targets[66] <= 12'h314; targets[67] <= 12'h315; targets[68] <= 12'h320; targets[69] <= 12'h321; targets[70] <= 12'h324; targets[71] <= 12'h325; targets[72] <= 12'h340; targets[73] <= 12'h341; targets[74] <= 12'h342; targets[75] <= 12'h345; targets[76] <= 12'h350; targets[77] <= 12'h351; targets[78] <= 12'h352; targets[79] <= 12'h354; targets[80] <= 12'h401; targets[81] <= 12'h402; targets[82] <= 12'h403; targets[83] <= 12'h405; targets[84] <= 12'h410; targets[85] <= 12'h412; targets[86] <= 12'h413; targets[87] <= 12'h415; targets[88] <= 12'h420; targets[89] <= 12'h421; targets[90] <= 12'h423; targets[91] <= 12'h425; targets[92] <= 12'h430; targets[93] <= 12'h431; targets[94] <= 12'h432; targets[95] <= 12'h435; targets[96] <= 12'h450; targets[97] <= 12'h451; targets[98] <= 12'h452; targets[99] <= 12'h453; targets[100] <= 12'h501; targets[101] <= 12'h502; targets[102] <= 12'h503; targets[103] <= 12'h504; targets[104] <= 12'h510; targets[105] <= 12'h512; targets[106] <= 12'h513; targets[107] <= 12'h514; targets[108] <= 12'h520; targets[109] <= 12'h521; targets[110] <= 12'h523; targets[111] <= 12'h524; targets[112] <= 12'h530; targets[113] <= 12'h531; targets[114] <= 12'h532; targets[115] <= 12'h534; targets[116] <= 12'h540; targets[117] <= 12'h541; targets[118] <= 12'h542; targets[119] <= 12'h543; end
这样,每一局游戏只需要随机生成一个范围在 0 ~ 119 之间的下标 index,即可得到对应的游戏目标 targets[index] 了。
下面,我们给出 Random.v 模块的基础端口代码。
Random.v1 2 3 4 5 6
module Random( input [ 0 : 0] clk, input [ 0 : 0] rst, input [ 0 : 0] generate_random, output [11 : 0] random_data );
其中:generate_random 信号来自控制模块,高电平有效。在时钟上升沿,如果发现 generate_random 信号为 1,则需要给出一个新的 12bits 随机数 random_data。一个最为简单的递增实现如下:
1 2 3 4 5 6 7 8 9 10 11 12
reg [7 : 0] index; always @(posedge clk) begin if (rst) index <= 0; else if (generate_random) begin if (index < 119) index <= index + 1; else index <= 0; end end assign random_data = target[index];
这样,每一局游戏的答案以 120 为周期进行循环。你可以在为 target 初始化时将数据随机排序(而不是现在的顺序排序),从而得到更加随机的结果。
接下来,我们需要引入硬件的伪随机。一个直观的思路就是将上一次的 index、用户输入 sw 以及当前系统运行的时间(可以维护一个计数器)作为因变量,控制 index 的产生逻辑。例如:
always @(posedge clk) begin if (rst) index <= 0; else index <= (index_old + sw_seed + timer_seed) % 120; end
现在,由于每一次用户的输入序列不同,以及对应的游戏时间不同,游戏仅有 rst 后的第一次答案固定,之后的答案都将随机产生。你需要将结合了用户输入 sw 以及当前系统运行的时间的 Random 模块正确接入通路,实现随机的题目生成功能。为此你可能需要为 Random 模块增加一些端口。
2.2.4.4 串口命令
为什么串口方向的练习会与串口没关系呢?这不就来了嘛!
我们希望为猜数字小游戏增加一些串口交互。你需要添加如下的内容:
a;:在串口界面输出当前游戏的答案 target_number。此时通过开关输入答案后游戏可以正常进入胜利状态。 n;:开始一局新的游戏。此时游戏的答案需要发生变化,计时器及核心状态机也需要改动。注意:n; 命令与 rst 信号的功能并不等价。 p;:暂停计时器。输入后游戏的计时器暂停工作,其他模块则正常进行。再次输入后计时器则会继续工作。你可以参考串口通信的教程完成本部分设计。为此,你需要自行设计部分模块,并修改数据通路。
题目 2-A-6
请根据以上内容,按要求完成本项练习。