P1 OK ?
好xdm,P1 有沒有 1h 通關!有沒有被 ISim 折磨到頭疼 ……
如果你很順利通過所有題目 + 附加題,那麼 dalao 就是你~
經過了上篇中提到的 P1 課下作業以及附加題那些練習之後,是時候在上機測試中展現自己的能力了。
到了第二次課上的測試環節,我們會遇到很多新的挑戰,時間比 P0 稍緊,題幹比 P0 長,題意還有可能不是 很清晰(例如第一次 P1 上機測試中的 翻餅),不過這些應該難不倒大家(很明顯作者第一次 P0 掛了)
我們展示 2020 秋的 P1 第 1、2 次上機題作為參考,如果你想把這些作為一次全真測試,那麼請先做完之後再看示例。另外,由於 P0 上機測試題僅有第 1、2 次的,所以考試難度不一定和這些題目相同,僅供參考~
另外,在這裡我們更新一下題號的 Definition:給定一個 x,P(x)s(y).Q(z) 為 P(x) 到 P(x+1) 的第 y 次測試的第 z 個進階題目,即上機測試題;P(x).Qs(y) 為 P(x) 課下第 y 個附加題
現在需要你用Verilog語言編寫一個投票表決器。投票者的類型分為 np(normal person),vip 和 vvip 三種,每種投票者的人數和一票所對應的權值如下表所示:
投票者類型人數每票權值np321vip84vvip116若得到的權值總和大於等於32,則結果為通過;否則,結果為不通過。
模塊埠定義如下:
信號名方向描述np[31:0]I每一位代表一個 np(高電平有效)vip[7:0]I每一位代表一個 vip(高電平有效)vvipI代表一個 vvip(高電平有效)resultO是否通過(高電平有效)提交要求:
這個題目可以 使用 for 循環 完成加法。我之前很少用 for 循環,在考場上現學現買一直過不去,後來助教提醒 需要清零,趕緊改了然後就通過了……
當然還有別的寫法,你可以模仿 C 語言直接寫 邏輯加法,或者模仿 【大番薯的石頭屋】 用 excel 自動填充代替自己複製代碼…… 反正如何實現都是可以的。
另外這個題是組合邏輯,寫法也有很多,這裡展示一種寫法,使用 assign 賦值:
module myvoter(
input [31:0] np,
input [7:0] vip,
input vvip,
output result
);
wire [31:0] cnt;
wire [31:0] cntnp;
wire [31:0] cntvip;
wire [31:0] cntvvip;
assign cntnp = np[0] + np[1] + np[2] + np[3] + np[4] + np[5] + np[6] + np[7] + np[8] + np[9] + np[10] + np[11] + np[12] + np[13] + np[14] + np[15] + np[16] + np[17] + np[18] + np[19] + np[20] + np[21] + np[22] + np[23] + np[24] + np[25] + np[26] + np[27] + np[28] + np[29] + np[30] + np[31];
assign cntvip = (vip[0] + vip[1] + vip[2] + vip[3] + vip[4] + vip[5] + vip[6] + vip[7]) << 2;
assign cntvvip = vvip << 4;
assign cnt = cntnp + cntvip + cntvvip;
assign result = cnt >= 32;
endmodule
CookHelper(P1s1.Q2) (折磨王)眾所周知,烹飪時對火候的把握是十分重要但又不容易掌握的。我們拿到了一種奇怪的食材,希望你能夠設計一個模塊來輔助烹飪。
簡單起見,我們把這種食材分為生、半熟、全熟三個正常狀態以及烹飪失敗的狀態。
在最初,食材處於生的狀態;
生的食材在油溫大於等於120且小於130時,進入半熟狀態;
半熟狀態的食材在油溫大於等於130且小於150時需要翻面1次;
翻面後,當油溫大於等於150且小於180時,食材進入全熟狀態。
當油溫超過食材下一階段所需油溫(對於全熟,下一階段所需油溫視為小於180)時,食材進入烹飪失敗的狀態。任何不恰當的翻面操作(1.翻面次數大於1次;2.未進行翻面就升溫至150度及以上;3.未在恰當時機進行翻面)都會導致食材直接進入烹飪失敗的狀態。其他情況下,食材維持當前狀態。
(注1:半熟、未翻面的食材在油溫大於等於130且小於150時不進行翻面,不會導致烹飪失敗。)
(注2:油溫低於當前狀態所需溫度時,若不進行翻面操作,則不會導致烹飪失敗。)
模塊在每個時鐘上升沿對油溫和翻面情況進行採樣(不關心其他情況下的輸入,Moore型FSM),並更新翻面提示信號和食材的當前狀態(2『b00:生,2『b01:半熟,2『b10:全熟,2『b11:失敗)。
若且唯若下述三個條件同時滿足時,翻面提示信號為高電平:1.當前採樣油溫大於等於130且小於150;2.當前食材處於半熟狀態;3.當前食材還未進行過翻面操作,且當前採樣時食材也未進行翻面。
模塊埠定義如下:
信號名方向描述clkI時鐘信號resetI異步復位信號temp[7:0]I當前油溫(無符號)flipI當前是否進行翻面操作status[1:0]O食材當前狀態need_flipO翻面提示信號提交要求
示例波形 1
示例波形 2
示例波形 2 說明
半熟、未翻面的食材在油溫首次大於等於130且小於150的時鐘上升沿即可進行翻面,無需等待下一時鐘上升沿,亦無需等待need_flip信號。也就是說,need_flip信號僅作指示使用,與食材的狀態轉移無關。
題目分析很遺憾,這個題目沒有標準通過代碼,因為找不到一個通過所有測試點的現場代碼。
這個題目是一個 比較複雜的狀態機,其實就是按照狀態機的各個狀態轉換進行判斷。由於我再做題的時候化簡了部分情況,導致狀態轉換條件寫的沒有什麼很好的邏輯形式,所以評測的時候一錯再錯,甚至延長了 30 分鐘也沒能完成這道題(那個時候我的 debug 能力很弱),最後勉強通過了一半的點。
這個題建議大家有能力的可以寫出來自己進行評測,或者組團寫完以後自己編寫測試集並進行對拍,如果你在很短的時間做出了這道題,就也很強的。
日期格式檢查(P1s1.Q3)現在需要你用Verilog語言編寫一個日期格式檢查工具。
合法的日期形如2020-9-1,2021.7.3,1999/10/22等。具體要求如下:
年份:1-4位數字,不允許前導0,不允許為0;
月份:1-2位數字,不允許前導0,值為[1,12]內的整數;
日期:1-2位數字,不允許前導0,值為[1,30]內的整數;
分隔符:允許』.『,』-『和』/『三種分隔符,日期內分隔符必須保持一致。
該模塊判斷復位後的輸入是否構成一個合法的日期(僅檢查上述格式要求,無需考慮日期的其他合理性)。輸入的讀取在時鐘上升沿進行。
模塊埠定義如下:
信號名方向描述clkI時鐘信號resetI異步復位信號(高電平有效,復位時將輸入記錄清空)in[7:0]I當前輸入字符的ASCII碼resultO當前輸入是否是合法日期提交要求
示例波形
題目分析這個題更循規蹈矩一些,很像 P1 課下作業中的 附加題。
特別注意在 P1 中的 狀態轉換條件,漏判一個都有可能導致一直 WA 到懷疑人生。還有 清零 的寫法,是 異步復位 還是 同步復位 一定要看清,其他的就是要搞清楚狀態轉換之間的關係、歸零的時候有沒有其他信號歸零。剩下的就是碼代碼嘞~
Verilog 實現(Author : cja) module DateChecker( //僅供參考,不確定一定正確
input clk,
input reset,
input [7:0] in,
output result
);
parameter WAIT = 0, YEAR = 1, GO_MON = 2, MONTH = 3, GO_DATE = 4, DATE = 5, END = 6;
integer cnt = 0, month = 0, date = 0;
reg [2:0] state = 0, next_state = 0;
reg [7:0] op = 0;
wire isdigit, isop, valid_month, valid_date;
assign isdigit = "0" <= in & in <= "9";
assign isop = in == "." | in == "/" | in == "-";
assign valid_month = 1 <= month & month <= 12;
assign valid_date = 1 <= date & date <= 30;
always @ (*) begin
case(state)
WAIT: next_state = isdigit & in != "0" ? YEAR : END;
YEAR: next_state = isop ? GO_MON :
isdigit & cnt < 4 ? YEAR : END;
GO_MON: next_state = isdigit ? MONTH : END;
MONTH: next_state = ~valid_month ? END :
isdigit ? (cnt < 2 ? MONTH : END) :
isop & in == op? GO_DATE : END;
GO_DATE:next_state = isdigit ? DATE : END;
DATE: next_state = ~valid_date ? END :
isdigit ? cnt < 2 ? DATE : END : END;
END: next_state = END;
endcase
end
always @(posedge clk or posedge reset) begin
if (reset) begin
state <= WAIT;
op <= 0;
cnt <= 0;
month <= 0;
date <= 0;
end
else begin
state <= next_state;
op <= isop & state == YEAR ? in : op;
cnt <= isdigit ? cnt + 1 : 0;
month <= (state == GO_MON | state == MONTH) & isdigit ? (month << 3) + (month << 1) + (in - "0") : month;
date <= (state == GO_DATE | state == DATE) & isdigit ? (date << 3) + (date << 1) + (in - "0") : date;
end
end
assign result = state == DATE & valid_date & valid_month;
endmodule
猜顏色(P1s2.Q1)「猜顏色」是一款有趣的小遊戲。下面簡要介紹它的部分規則:
有6種顏色的大頭針(每種顏色的大頭針可以有多個),用戶需要在4個輸入槽內各放置一個大頭針。在每輪遊戲中,系統會對用戶的輸入情況進行反饋。當用戶的答案與正確答案吻合(每個槽內大頭針的顏色與該槽答案均匹配)或遊戲超過8輪用戶仍作答錯誤,則遊戲結束。
下面定義兩種反饋類型:
下面需要你用Verilog語言編寫一個模塊,實現對遊戲中用戶輸入的答案的反饋(不考慮遊戲是否已經結束)。
模塊埠定義如下:
信號名方向描述ans0[2:0]I第0個位置上答案的顏色編號ans1[2:0]I第1個位置上答案的顏色編號ans2[2:0]I第2個位置上答案的顏色編號ans3[2:0]I第3個位置上答案的顏色編號input0[2:0]I第0個位置上用戶輸入的顏色編號input1[2:0]I第1個位置上用戶輸入的顏色編號input2[2:0]I第2個位置上用戶輸入的顏色編號input3[2:0]I第3個位置上用戶輸入的顏色編號both_correct[2:0]O用戶顏色和位置均正確的輸入的個數color_correct[2:0]O用戶僅顏色正確的輸入的個數提交要求
樣例數據
ans0-ans3分別為0、1、4、2,input0-input3分別為1、4、1、2時,both_correct為1,color_correct為3。
題目分析這題一看挺唬人,但是仔細想想其實不難。很明顯這是一道組合邏輯題,不過有一點小坑就一定注意是用各個 input 匹配各個 ans ,而不是用各個 ans 匹配各個 input;而且注意是同一個位置和對應位置答案不同的時候就不再匹配和其他位置的顏色是否相同。當時有一些同學前幾個點過不去,就是因為上述情況在作怪。
其餘的就沒什麼了,下面直接放出來代碼吧~ :
Verilog 實現1(Author : lxl) module guess(
input [2:0] ans0,
input [2:0] ans1,
input [2:0] ans2,
input [2:0] ans3,
input [2:0] input0,
input [2:0] input1,
input [2:0] input2,
input [2:0] input3,
output [2:0] both_correct,
output [2:0] color_correct
);
reg c_cor0;
reg c_cor1;
reg c_cor2;
reg c_cor3;
initial begin
c_cor0 = 0;
c_cor1 = 0;
c_cor2 = 0;
c_cor3 = 0;
end
always @* begin
if (input0 == ans0) c_cor0 = 0;
else if (input0 != ans0) begin
c_cor0 = (input0 == ans1)? 1:
(input0 == ans2)? 1:
(input0 == ans3)? 1:0;
end
if (input1 == ans1) c_cor1 = 0;
else if (input1 != ans1) begin
c_cor1 = (input1 == ans0)? 1:
(input1 == ans2)? 1:
(input1 == ans3)? 1:0;
end
if (input2 == ans2) c_cor2 = 0;
else if (input2 != ans2) begin
c_cor2 = (input2 == ans0)? 1:
(input2 == ans1)? 1:
(input2 == ans3)? 1:0;
end
if (input3 == ans3) c_cor3 = 0;
else if (input3 != ans3) begin
c_cor3 = (input3 == ans0)? 1:
(input3 == ans1)? 1:
(input3 == ans2)? 1:0;
end
end
assign both_correct = (ans0 == input0) + (ans1 == input1) + (ans2 == input2) + (ans3 == input3);
assign color_correct = c_cor0 + c_cor1 + c_cor2 + c_cor3;
endmodule
為什麼 自己現場寫出來的代碼 這麼醜……還有,不要學這個樣例,切記 代碼複製 的禁忌,很容易出問題的。
Verilog 實現2(Author : cja) module guess(
input [2:0] ans0, ans1, ans2, ans3,
input [2:0] input0, input1, input2, input3,
output [2:0] both_correct, color_correct
);
reg [2:0] in [3:0];
reg [2:0] ans [3:0];
reg [2:0] k, q;
reg [3:0] temp;
integer i, j;
always @* begin
in[0] = input0;
in[1] = input1;
in[2] = input2;
in[3] = input3;
ans[0] = ans0;
ans[1] = ans1;
ans[2] = ans2;
ans[3] = ans3;
end
always @* begin
k = 0; temp = 0;
for(i = 0; i < 4; i = i + 1) begin
for(j = 0; j < 4; j = j + 1) begin
if(in[i] == ans[j]) begin
if(~temp[i] && in[i] != ans[i] && i != j) begin
k = k + 1;
temp[i] = 1;
end
end
end
end
end
always @* begin
q = 0;
for(i = 0; i < 4; i = i + 1)
if(in[i] == ans[i]) q = q + 1;
end
assign both_correct = q;
assign color_correct = k;
endmodule
這代碼寫的就好看很多了~
five狀態機(P1s2.Q2)用Verilog語言編寫一個five狀態機。
一個five有四種狀態:sleeping、getting up、eating、entertaining,分別用兩位編碼2『b00、2』b01、2『b10、2』b11表示。five的初始狀態為sleeping。
一個five只會從外界接收四種信號:alarm、sleepy、hungry、full,分別用兩位編碼2『b00、2』b01、2『b10、2』b11表示。信號在時鐘上升沿採樣。
sleeping的five會被alarm叫醒,進入getting up狀態;getting up的five會在兩個時鐘周期後進入entertaining狀態;entertaining的five會在sleepy的時候進入sleeping狀態、在hungry的時候進入eating狀態;eating的five會在sleepy的時候進入sleeping狀態、在full的時候進入entertaining狀態;其他情況下,five維持當前狀態。
模塊埠定義如下:
信號名方向描述clkI時鐘信號resetI異步復位信號sig[1:0]I當前信號status[1:0]Ofive當前狀態提交要求
示例波形
題目分析
這題目和第一次 P1 上機測試時序邏輯 翻餅 那題那個簡直沒法比,狀態轉換也很簡潔。幾乎就是文字敘述的狀態轉換表,一句一句填上就好了。異步復位的寫法相比大家也很熟悉了,所以算是常規操作。最後注意那個 getting up 狀態的時候兩個周期以後計數要清零,防止回到再次回到 getting up 狀態以後無法保證兩個周期再輸出。(說實話這題 挺 five 的 (bushi
Verilog 實現1(Author : lxl) `define s 2'b00
`define g 2'b01
`define ea 2'b10
`define en 2'b11
module five(
input clk,
input reset,
input [1:0] sig,
output reg [1:0] status
);
reg cnt = 0;
always @(posedge clk, posedge reset) begin
if(reset) begin
s <= `s;
cnt <= 0;
end
else begin
case(status)
`s:begin
if(sig == 2'b00) status <= `g ;
end
`g:begin
if(cnt == 1) begin
cnt <= 0;
status <= `en;
end
else begin
cnt <= cnt + 1;
status <= `g;
end
end
`ea: begin
if(sig == 2'b01) status <= `s;
else if(sig == 2'b11) status <= `en;
end
`en: begin
if(sig == 2'b01) status <= `s;
else if(sig == 2'b10) status <= `ea;
end
endcase
end
end
endmodule
*注意:`define + name + 參數 之後不加 ;。
Verilog 實現2(Author : cja) module five(
input clk,
input reset,
input [1:0] sig,
output [1:0] status
);
parameter sleep = 0, getup = 1, eat = 2, entertain = 3;
reg [1:0] s = 0;
integer cnt = 0;
always @ (posedge clk, posedge reset) begin
if(reset) begin
s <= sleep;
end
else begin
case(s)
sleep:s <= sig == 0 ? getup : sleep;
getup:s <= cnt == 1 ? entertain : getup;
entertain: s <= sig == 1 ? sleep :
sig == 2 ? eat : entertain;
eat:s <= sig == 1 ? sleep :
sig == 3 ? entertain : eat;
endcase
cnt <= s == getup ? cnt + 1 : 0;
end
end
assign status = s;
endmodule
電子郵件地址檢查(P1s2.Q3)用Verilog語言編寫一個簡易的電子郵件地址格式檢查工具。
合法的電子郵件地址形如taco@outlook.com,star@126.com 等。具體要求如下:
該模塊判斷復位後的輸入是否構成一個合法的電子郵件地址(僅檢查上述格式要求,無需考慮地址的其他合理性)。輸入的讀取在時鐘上升沿進行。
模塊埠定義如下:
信號名方向描述clkI時鐘信號resetI異步復位信號(高電平有效,復位時將輸入記錄清空)in[7:0]I當前輸入字符的ASCII碼resultO當前輸入是否是合法電子郵件地址提交要求
示例波形
題目分析這個題也很像 P1 課下作業中的 附加題,或者先導課程 CPU_CHECKER。
如果你有大約 25min 的時間,這個題目就可以過了。具體細節前面幾題中已經闡明,這裡不細說了,在這裡貼出來一份代碼給大家參考:
Verilog 實現(Author : cja): module MailChecker(
input clk,
input reset,
input [7:0] in,
output result
);
parameter WAIT = 0, USER = 1, W = 2, A = 3, END = 4, ERROR = 5;
reg [2:0] s = 0;
integer cnt = 0;
wire isdigit = "0" <= in & in <= "9";
wire isalpha = "a" <= in|8'd32 & in|8'd32 <= "z" ;
always @ (posedge clk, posedge reset) begin
if(reset) begin
s <= 0;
cnt <= 0;
end
else begin
case(s)
WAIT: begin
s <= isalpha ? USER : ERROR;
cnt <= isalpha ? 1 : 0;
end
USER: begin
s <= isalpha | isdigit ? USER :
in == "@" ? cnt >= 3 ? W : ERROR : ERROR;
cnt <= isalpha | isdigit ? cnt + 1 :
in == "@" ? 0 : cnt;
end
W: begin
s <= isalpha | isdigit ? W :
in == "." ? cnt >= 1 ? A : ERROR : ERROR;
cnt <= isalpha | isdigit ? cnt + 1 :
in == "." ? 0 : cnt;
end
A: s <= isalpha ? END : ERROR;
END: s <= isalpha ? END : ERROR;
ERROR: s <= ERROR;
default: s <= WAIT;
endcase
end
end
assign result = (s == END);
endmodule
P1 上機的一些 Tip:熟悉 異步復位、同步復位的寫法。
注意時間,但是也不要過於糾結時間,課下多嘗試面向評測機 debug,這樣上機時間比較充裕。
狀態轉換最好清晰,推薦使用宏定義。
以計組上機經驗來說,如果第 2 題很陰間,那可以去嘗試第 3 題,不過每次的測試都不一樣。
好啦,以上是 重製版的第 4 期【我的計組生活】
希望能有幫助到你哦!!不介意的童鞋可以來一波分享,也可以來點讚 或 在看,甚至是不是還可以來一波打賞~
如果看到有錯誤還請一定聯繫作者更改,可以在後臺直接進行留言~,拜託各位大佬了!
祝大家好運,P1 測試都 AK !
謝謝大家~